Introduction

Welcome to the Pijul book, an introduction to Pijul, a distributed version control system that is at the same time theoretically sound, fast and easy to learn and use.

A version control system is a tool that tracks changes in your files, can revert them, and can merge them with your coauthors' changes.

Why Pijul?

Pijul for Git users

The main difference between Pijul and Git is that Pijul deals with changes (or patches), whereas Git deals only with snapshots (or versions).

There are several advantages to using patches. First, patches are the intuitive atomic unit of work. As such, they are easier to understand than commits. And actually, Git users often reason in terms of patches, displaying commits as differences between snapshots.

Patches can be merged according to intuitive formal axioms (more on that below). This yields several nice properties, which we explain now.

Patch commutation

In Pijul, for any two patches A and B, either A and B commute, (in other words, A and B can be applied in any order), or A depends on B, or B depends on A.

  • [Use case: early stage of a project] Patch commutation makes Pijul a highly forgiving system, as you can "unapply" (or "unrecord") changes made in the past, without having to change the identity of new patches (without "rebasing", in git terms).

    This tends to happen pretty often in the early stages of a project, when most things are still uncertain. With Pijul, exploring new features and new implementations comes at no extra cost in time.

    Git, however, would prompt you to think carefully before sharing a branch with others, that contain many new features.

  • [Use case: the project becomes stable] As your project grows, patch commutation saves even more time: imagine a project with two main branches, a stable one containing only the original product, and bugfixes, and an unstable one, where new features are constantly added.

    The team working on the unstable branch is likely to discover old bugs, and fix them in the stable branch too.

In Pijul, maintainers of the stable branch can simply pull only the patches they are interested in. Those patches do not change when imported, which means that pulling new patches in will work just as expected.

In Git, importing just a few commits from another branch is called "cherry-picking", and involves changing the identity (the hash) of those commits. This usually works the first time. However, when done again, as the maintainer of a stable branch probably wants to do, this often causes counter-intuitive conflicts. The reason is that Git has no way to know that these changes come from the same commit, since the commit has changed its identity after being cherry-picked.

Associativity

In Pijul, patch applications is an associate operation, meaning that applying some patch A, and then a set of patches (BC) at once, yields the same result as applying (AB) first, and then C.

With branches, the first scenario looks like this: Bob creates A (the orange commit), while Alice creates B (in blue), C (in green), and Bob finally merges both B and C at once.

The second scenario would look like the following, with Bob creating commit A, and then pulling B. At that moment, Bob has both A and B on his branch, and wants to pull B from Alice.

Note that this is different from patch reordering: here, we apply A, then B, then C, in the same order in both scenarios.

Using math words such as "associative" for such a simple operation may sound like nitpicking, because intuition suggests that it should always be the case. However, Git doesn't guarantee that property, even if A, B, and C do not conflict (see this example for more).

Predictable merges

The theory behind Pijul is relatively simple, and can be roughly seen as coauthors trying to edit a graph of lines collaboratively, by only adding new vertices and edges, or changing the labels of existing edges.

A little more work is required to show that this can be made into a fast system with the properties we want on patches, but that's about it.

The definition of merging two independent patches A and B into repository R is also quite simple: R+{A, B} is the unique smallest repository containing all of R, plus all the changes of A and B.

This makes Pijul extremely predictable (modulo a few possible remaining bugs, this project is still beta).

This stands in contrast with Git, which uses heuristics for a number of important operations:

  • In Git, even if one is careful enough to plan branches in advance, the solution to a merge is not guaranteed to be unique. Git just picks one arbitrarily. Occasionally, Git will merge lines in unexpected places, like in this example. This is pretty bad, and can happen on real life source code.

    On security-sensitive code, this might even turn into serious financial problems, even if all contributors are trustworthy.

    The solution to this is to ignore the testing efforts made on both branches, and restart the testing-and-debugging loop from scratch, with potentially serious implications in terms of time and money.

  • Git has no true notion of files, and uses heuristics to recover files from changes. This is certainly better than its predecessors (such as SVN), but sometimes causes artificial conflicts.

Pijul for Darcs users

Pijul is mostly a formally correct version of Darcs' theory of patches, as well as a new algorithm for merging changes. Its main innovation compared to Darcs is to use a better data structure for its pristine, allowing for:

  • A sane representation of conflicts: Pijul's pristine is stored in a "conflict-tolerant" data structure. Many patches can be applied to it, and the presence or absence of conflicts are only computed afterwards, by looking at the pristine.

  • Fast algorithms: Pijul's pristine can be seen as a "cache" of applied patches, to which new patches can be applied directly, without having to compute anything on the repository's history.

However, Pijul's pristine format was designed to comply with axioms on a specific set of operations only. As a result, some of darcs' features, such as darcs replace, are not (yet) available.

Better conflicts for everyone

Conflicts are a normal thing in the internal representation of a Pijul repository. Actually, after applying new patches, we even have to compute where the conflicts are.

In particular, patches editing sides of a conflict can be applied without resolving the conflict. This guarantees that no information ever gets lost.

This is different from both Git and Darcs:

  • In Git, conflicts are not really handled after they are output to files. For instance, if one commits just after a conflict occurs, git will commit the entire conflict (including markers).

  • In Darcs, conflicts can lead to the exponential merge problem, which might cause it to take several hours to merge even a two-lines patch.

Installing

Releases

Currently, as Pijul is still in beta, the main channel to get it is the Rust Language package manager, Cargo. See here for installing Cargo (which comes with Rust), and then run the following command in your terminal:

cargo install pijul

Distribution packages

NixOS

nix-env -iA nixos.pijul

Snapcraft

Package provided by Zygmunt Krynicki.

https://uappexplorer.com/snap/ubuntu/pijul

FreeBSD / DragonflyBSD

https://www.freshports.org/devel/pijul/

Windows

Major releases of Pijul are built for Windows, and available for download on https://pijul.org/downloads/#windows-binaries

Getting started

Here is how to start a project with Pijul: first run the following command:

pijul init

In the project's directory. This creates a directory called ".pijul", and initialises a few things.

Then, add a few files to track. For a Rust project, this could be for instance:

pijul add Cargo.toml src/lib.rs

Finally, create a patch by recording your changes:

pijul record

Let's see what happened under the hood in this simple example:

Definitions

Pijul works in four different places of your hard drive: a working copy, where you can edit files, a set of patches, a tree, which is the set of files currently tracked by Pijul, and a pristine, which is a representation of the current recorded version of the repository.

The changes between the pristine and the working copy are used to produce patches, which can be then applied to the pristine. Patches encode edits made to files of the repository (line insertions and deletions), as well as file additions, deletions and name changes.

At any time, the pristine can be output into the working copy, for instance after receiving new patches, or to cancel changes and reset the working copy to its recorded state.

So, the first command of the above example, pijul init, initialised an empty pristine. pijul add then updated the tree, to tell Pijul to start tracking files "Cargo.toml" and "src/lib.rs". Finally, pijul record compared the (currently empty) repository with the working copy, producing a patch as a result, and applied this patch to the pristine.

Next steps

In the next chapter, we will see how to exchange patches with others.

Collaboration with Pijul doesn't have to be centralised, and is not even made easier by centralisation, as patches allow for a completely distributed workflow, which can happen even by email exclusively.

However, the authors of Pijul provide a free web service called the Nest, as one way to share patches with collaborators.

Working with others

Although one can use Pijul to work alone, for instance to store the history of changes of a project, or synchronise work with another machine, it is mainly useful (and more efficient than other solutions) when working with other people.

The only way to collaborate with others in Pijul is to send and receive patches, and apply them to one's repository. When working on an existing project, is might be necessary to clone it first. For instance, in order to clone Pijul's main repository, you can run the following command:

pijul clone https://nest.pijul.com/pijul_org/pijul

This just downloads all the patches currently in the remote repository, applies them all, and outputs the result to a new working copy. Further patch exchange can be done with the two commands pull (receive patches) and push (send patches), as we explain now.

Between local repositories

Patches can also be exchanged between local repositories, in both directions (push and pull). As an example, let's create two repositories, and exchange two patches between them:

mkdir a
cd a
pijul init
echo "blabla" > file
pijul add file
pijul record
cd ..

pijul clone a b
cd b
cat file # should contain "blabla"
echo "blibli" >> file
pijul record
pijul push ../a
echo "one extra line" >> file
pijul record

cd ../a
pijul pull ../b

SSH

Pijul can work with remote repositories over SSH just like local repositories. A working Pijul needs to be installed on the remote machine, though.

As an example, if you have an account called "me", and a repository named "repo" under that account on nest.pijul.com, you can run for instance:

pijul clone me@nest.pijul.com:repo

To clone your repository, and:

pijul push me@nest.pijul.com:repo

To send your local patches to your repository there. Just as with a local repository, pulling patches can also be done over SSH:

pijul pull me@nest.pijul.com:repo

HTTP

Pijul is able to download patches from HTTP and HTTPS servers. In the above example, receiving new patches from Pijul's main repository can be done by running:

pijul pull https://nest.pijul.com/pijul_org/pijul

However, Pijul is not (yet) able to push patches to an HTTP URL.

Signing patches

In any distributed system, it is important to be able to tell your coauthors who you are in a secure way. One possible way to do this is to sign your patches.

If you choose to do so, others will expect your patches to be signed, and will reject attempts to impersonate you.

Signing patches doesn't prevent others from stealing your patches, ripping off the patches' signatures and signing them as their own, though.

After a keypair is generated, pijul record will automatically sign all your patches.

Generating a secret key

Pijul can generate signing keys, either per repository (the keys are then stored in the .pijul of that repository) or globally (in which case the keys are stored in your home directory). For security reasons, patches can be signed by only one person, even patches with multiple authors.

To generate a signing key in you user account, run the following command in a terminal:

pijul keys --generate-signing

Uploading your public key to the Nest

For security reasons, the Nest will never offer you any interface to upload your public key.

Instead, Pijul can be asked to convince the Nest (or actually any other server) that you really own the secret key associated to your public key. Once you have a secret key, this can be done by running the following command in a terminal:

pijul keys --upload-to me@nest.pijul.com

Internally, that command asks the Nest to generate a challenge string, signs it, and replies with the signature. If the signature is correct, the Nest will know the public key, and recognise it as yours.

Why not PGP?

Pijul users with an experience with public keysystems will probably wonder why Pijul uses its own signature system instead of PGP. This choice was carefully considered, and a prototype PGP library was even written as part of the Pijul project as some point.

However, PGP has several drawbacks:

  • It is quite bad from a privacy perspective, because all its users shout out to the world who their friends are, when they met, etc.

  • The PGP protocol specification is really ambiguous, which is terrible for security, and not great for user experience, portability, and interoperability with existing software.

  • The main free implementation of PGP, GnuPG, is an extraordinarily complex and large project, where even non performance-critical parts are written in C by mostly a single person. From a security and user experience point of view, this is possibly suboptimal.

Conflicts

An important part of working with others is disagreeing. On text files (such as source code), work can sometimes be split beforehand in such a way that disagreements on a single line in a file never occurs.

However, most creative teams don't have everything planned in advance, and their members often improve each other's code. This might result in conflicts.

In Pijul, there are two kinds of conflicts inside a file:

  • When two different authors add lines at the same position in a file, and it is impossible to tell which comes first in the file.

  • When one author adds a line in a block of text or code, while another author deletes that block.

Conflicts can also occur between files:

  • When authors give the same name to two different files.

  • Or when they rename the same file to two different names.

One of the main features of Pijul is that its internal representation of repositories models conflicts. Patches can be applied to a conflicting repository, leaving the conflict resolution for later.

The Nest

The Nest, hosted at nest.pijul.com, is a platform for hosting and publishing Pijul repositories. This chapter explains how to use it in greater detail.

Creating an account

In order to create an account on the Nest, go to https://nest.pijul.com, and fill in the signup form on the front page.

Alternatively, one can also signin with a GitHub, a Google or a Twitter account, with the same effect.

After confirming your account (if you chose to signup by email), you will be taken to your profile settings page, which allows you to add login details, such as SSH keys, and to create repositories.

At any time, you can go to your profile settings page by clicking on your login, or on the in the top right corner of the page.

Uploading public keys

Pijul and the Nest use two kinds of keypairs (public and secret): keys for signing patches, and keys for SSH access.

SSH public keys

The Nest's profile settings page contains a field to add SSH public keys. At the moment, supported formats are Ed25519 and RSA. Pijul can handle password-protected keys, but cannot talk to the OpenSSH key agent (essentially because of diverging views about cryptography).

Signing keys

While you're here, you might as well setup a key to sign your patches. There is a really easy and fast procedure to do so, as explained here.

Creating repositories

In order to create a repository on the Nest, you first have to go to your profile settings page (by clicking on your user name or on the in the top right corner of the page).

Then, just enter the name of your new repository in the field at the bottom of the list of your repositories, and voilà! You are ready to push patches directly to your new repositories (no need to clone it, patches can be pushed directly).

Reference

This chapter contains a detailed manual of individual Pijul commands. More succinct help about individual commands can also be obtained directly from a terminal by running pijul --help.

Help for individual commands can also be obtained in the same way. Let's take record as an example. The command to get help on record is pijul record --help.

pijul add

Add files and directories to the tree

Usage

pijul add [-h | --help] [--recursive] [-V | --version] [--repository <repository>] [<file>…]

Description

Adds directories and files to the tree, which is the set of files currently tracked by Pijul.

The tree's structure is supposed to mirror almost exactly the filesystem, with a few extra bits of information to synchronise it with the pristine.

Example

When starting a Rust project, it is common to add Cargo.toml and src/lib.rs from the repository root:

pijul add Cargo.toml src/lib.rs

Options

  • -h, --help

    Print a help message and exit.

  • --recursive

    Recursively add all the files in the directories in argument <file>…

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • <file>…

    A list of files to add to the tree.

pijul apply

Apply patches by their hash or patch file

Usage

pijul apply [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [<patch>…]

Description

This command is essentially meant to be called by pijul push over SSH.

It applies patches to a branch in the pristine, either to the current branch (if --branch is not specified), or to branch <branch>.

If that branch does not exist, it will be created.

<patch> must be a list of patch hashes or internal identifiers. If empty, gzipped-compressed patch files are read from the standard input. In other words, running pijul apply hashhashhash is equivalent on Unix to running cat .pijul/patches/hashhashhash.gz | pijul apply.

Example

Apply the first patch in Pijul's development repository:

pijul apply AbaeCc9Y9jxGRgkapqjMv66dfZEvSUf3biXZa2LpcYx-CY646dQ9HFmL4R6x9Mez_cCz5QwW4w38P1O4bwVsjjI

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Apply the patches to branch <branch> instead of the current branch.

  • <patch>…

    A list of patch hash or internal identifiers to apply to the branch.

pijul blame

Annotate a file with the patch that last touched each line

Usage

pijul blame [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] <file>

Description

Prints an annotated version of the file named <file> in the pristine on the standard output, where each line is prefixed by the patch that introduced it (or the last patch that modified it).

This command doesn't take the version of <file> in the working copy, and only considers the pristine.

Example

From the repository root, with a file named Cargo.toml in directory pijul:

pijul blame pijul/Cargo.toml

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Annotate the file in branch <branch> instead of the current branch.

  • <file>

    The name of the file to annotate

pijul checkout

Switch to a branch

Usage

pijul checkout [-h | --help] [-V | --version] [--repository <repository>] <branch>

Description

Reset the working copy to branch <branch> of the pristine, and set that branch as the current branch.

Example

From the repository root:

pijul checkout master

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • <branch>

    The branch of the pristine to reset the working copy to.

pijul clone

Clone a repository into a new directory

Usage

pijul clone [-h | --help] [-V | --version] [ --from-branch <from_branch> ] [ (-p | --port) <port> ] [ --to-branch <to_branch> ] <from> [to]

Description

Clone a single branch of a remote repository, i.e. initialise a new repository, and apply all patches from a branch from the remote repository. This is equivalent to pijul init, followed by pijul pull.

Example

From outside a repository:

pijul clone https://nest.pijul.com/pijul_org/pijul

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --from-branch <from-branch>

    Clone branch <from-branch> from the remote repository, instead of the default branch (which is "master").

  • <from>

    A URI to clone. At the moment, only local paths, SSH paths (such as me@example.com:path/to/repository) and HTTP(S) URLs are supported.

  • -p <port>, --port <port>

    If the repository is accessed over SSH, the port of the remote SSH server, defaulting to 22 (the standard SSH port).

  • --to-branch <branch>

    Store this branch as <branch> instead of using the same name as the remote branch.

  • [to]

    Path to the local directory where the repository will be cloned, defaulting to the current directory followed by the basename of the URI in .

pijul delete-branch

Delete a branch from the pristine

Usage

pijul delete-branch [-h | --help] [-V | --version] [--repository <repository>] <branch>

Description

Delete a branch from the pristine. This can only be done if there is at least one other branch (i.e. if there are at least two branches before running this command).

Example

If the pristine has two branches, "master" and "unstable":

pijul delete-branch unstable

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository

    Don't run this command in the current directory, run it in <repository> instead.

  • <branch>

    The name of the branch to be deleted.

pijul diff

Show changes since the last recorded version

Usage

pijul diff [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [prefix…]

Description

Print the difference between the pristine and the working copy, or in other words, what would be recorded if record were called now.

This command can compare another branch than the current one using the --branch argument, and can compare the directory or file in <prefix> instead of the whole directory.

Example

Show all the changes in the "src" and "tests" directories.

pijul diff src tests

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Compare branch <branch> instead of the current branch (the default).

  • [prefix…]

    A (possibly empty) list of paths in the repository to compare with the pristine. If empty, the whole repository is compared.

pijul dist

Creates an archive of the repository

Usage

pijul dist [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] -d <archive>

Description

Creates a gzipped tarball of all the files in branch <branch> (or in the current branch if --branch is missing), and save the result in a file named <archive>.tar.gz.

Example

The following produces a file named "pijul-0.8.0.tar.gz":

pijul dist -d pijul-0.8.0

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Fork branch <branch> instead of the current branch (the default).

  • -d <archive>

    Basename of the archive, without the .tar.gz extension.

pijul fork

Fork a branch into a new one.

Usage

pijul fork [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] <to>

Description

Create a new branch with the same contents as branch <branch>, or as the current branch if --branch is not specified.

Branches in Pijul are light, and running this command writes very few bytes on the disk.

Note that the purpose of branches in Pijul is quite different from Git, since Git's "feature branches" can usually be implemented by just patches.

Example

When initialising a repository, a branch called "master" is created. To create its first fork, and call it "unstable", do:

pijul fork unstable

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Fork branch <branch> instead of the current branch (the default).

  • [to]

    Name of the new branch.

pijul help

Display help information about Pijul

Usage

pijul help [subcommand]

Description

With no other option, pijul help shows the synopsis of all pijul subcommands. When [subcommand] is a a valid pijul subcommand, shows the detailed help for that subcommand.

Example

pijul help record

Options

  • [subcommand]

    Subcommand to print help of.

pijul init

Initialise a new repository

Usage

pijul init [-h | --help] [-V | --version] <directory>

Description

Initialise a new repository in directory <directory>, if specified, or in the current directory else.

Example

pijul init

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • <directory>

    The directory of the new repository.

pijul keys

Manage SSH and signing keys

Usage

pijul keys [-h | --help] [-V | --version] [--generate-ssh] [--generate-signing] [--local]  [(-p | --port) <port>] [--repository <repository>] [--upload-to <upload>]

Description

When called with --generate-ssh or --generate-signing, generate SSH and signing keys. When called with --upload-to <upload>, upload the signing public key to a key server.

Example

pijul keys --generate-signing

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --generate-ssh

    Generate a SSH key

  • --generate-signing

    Generate a patch signing key. See here for more details.

  • --local

    Save the keys to the local repository instead of your home directory. This might be useful for managing several identities, but having several key pairs scattered around your folders is usually not a good idea.

    Make sure to know what you are doing when using this option.

  • -p, --port

    The port of the remote SSH server when using --upload-to. If not specified, this defaults to 22 (the standard SSH port).

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --upload-to

    Upload the signing key to a key server, such as nest.pijul.com.

pijul changes

List all the patches in a branch

Usage

pijul log [-h | --help] [-V | --version] [ --hash-only ] [ --internal-id <internal-id>… ] [--repository <repository>] [--branch <branch>] [--grep <regex>]
================================
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
pijul changes [-h | --help] [-V | --version] [ --hash-only ] [ --internal-id <internal-id>… ] [--repository <repository>] [--branch <branch>]
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Description

Prints the list of all patches applied to branch <branch> if --branch is present, or to the current branch else. The patches are printed in the reverse order in which they were applied to the branch, and the information printed includes patch hashes, internal identifiers, among others.

If desired, a restricted subset of the changes, specified by --internal-id, can be listed. Here, <internal-id> is a list of internal patch identifiers, where an internal patch identifier is of the form tp4Jz1j2PEY.

--hash-only prints a list of patches in a machine-readable format, to be exchanged when comparing patchsets between repositories.

Example

From the repository root:

pijul changes

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Print changes on branch <branch> instead of the current branch.

  • --hash-only

    Print the changes in binary format. This option is essentially meant to be used when calling this command remotely over SSH.

  • --internal-id <internal-id>…

    Restrict the list to the set of patches specified by <internal-id>. One could for example run

    pijul changes --internal-id EuTRmk0mzNk tp4Jz1j2PEY
    
  • --grep <regex>

    Select only patches matching the supplied regular expression (may appear several times).

pijul ls

List files in the repository

Usage

pijul ls [-h | --help] [-V | --version] [--repository <repository>] [dir]…

Description

Lists all the files in directory <dir>, that are currently tracked in repository <repository> (or the repository in the current directory if --repository is missing).

Example

pijul ls src tests

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • [div]…

    List of directories to list. If this list is empty, the whole directory is listed.

pijul mv

Move and rename files and directories

Usage

pijul mv [-h | --help] [-V | --version] [--repository <repository>] <files>…

Description

Move and/or rename files and directories. The files listed in <files> must all be in the tree. <files> must contain at least two paths.

If <files> contains exactly two paths, and the second path is an existing directory, the first file or directory is moved to the second one. If the second path is not an existing directory, the name of the first file or directory is changed to the second one.

If <files> contains n > 2 paths, the last path must be an existing directory, and the first n - 1 files or directories are moved to the last path.

Example

pijul mv a b

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • <files>…

    List of at least two paths, as explained in the description above.

pijul patch

Output the compressed encoding of a patch (in binary)

Usage

pijul patch [-h | --help] [-V | --version] [--repository <repository>] [--bin] <patch>

Description

Output the patch to the standard output:

  • in gzip-compressed binary if --bin is supplied
  • in text else (may lose information). Note that the text representation is merely a pretty-printed representation, and is not sufficient to apply the patch.

This command is meant to be called on a remote repository over SSH.

Example

Print the first patch of the repository:

pijul patch $(pijul log --hash-only | sed -n "s/\(.*\):.*/\1/p" | head -n 1)

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • <patch>

    Hash of the patch to be printed.

pijul pull

Pull a patch from a remote repository

Usage

pijul pull [-h | --help] [-V | --version] [--repository <repository>] [--from-branch <from_branch>] [(-p | --port) <port>] [--to-branch <to_branch>] [--set-remote <set-remote>] [--set-default] [-a | --all] [remote]

Description

Pull patches from a remote repository to a branch of a local repository to a branch. Patch selection is either interactive, or includes all patches in the current branch if -a (or --all) is present.

If the <to_branch> does not exist in the local repository, it will be created. If the <from_branch> does not exist in the remote repository, this command does nothing.

This command can also save remote repositories, in two different ways:

  • If --set-default is specified, the remote destination becomes the default destination, and it is not necessary to give it a name. Subsequent pulls to the same address can be done by simply running pijul pull.

    It is not necessary to give a name (using --set-remote) to the default destination, although it is possible.

  • If --set-remote is specified, the remote destination is saved as <set-remote>, and can be reused in subsequent commands. For example, after calling pijul pull me@nest.pijul.com:example --set-remote nest, pulling from the same address (me@nest.pijul.com:example) again can be done by running pijul pull nest.

Saved remote repositories are written to ".pijul/meta.toml" (at the root of the repository).

Examples

pijul pull me@nest.pijul.com:example --set-remote nest
pijul pull nest -a

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --from-branch <from-branch>

    Pull patches from branch <from-branch> in the remote repository, instead of the default branch ("master").

  • -p <port>, --port <port>

    If the remote repository is accessed over SSH, the port of the remote SSH server, defaulting to 22 (the standard SSH port).

  • --to-branch <to-branch>

    Pull patches to branch <to-branch> in the local repository, instead of the current branch.

  • --set-remote <set-remote>

    Save this remote repository as <set-remote>. In subsequent commands, name <set-remote> can be used instead of this URI.

  • --set-default

    Make this remote repository the default. If subsequent commands involving remote repositories are called without a URI, the default remote repository will be used as the URI.

  • -a, --all

    Don't select patches to pull interactively, pull all patches instead.

  • [remote]

    Name or URI of the remote repository. A name can be for instance "nest", while a URI can be a HTTP or HTTPS URL, or an SSH address (such as "me@nest.pijul.com:repository"), or a local path.

pijul push

Push a patch to a remote repository

Usage

pijul push [-h | --help] [-V | --version] [--repository <repository>] [--from-branch <from_branch>] [(-p | --port) <port>] [--to-branch <to_branch>] [--set-remote <set-remote>] [--set-default] [-a | --all] [remote]

Description

Push patches from a branch of a local repository to a branch of a remote repository. Patch selection is either interactive, or includes all patches in the current branch if -a (or --all) is present.

If the <to_branch> does not exist in the remote repository, it will be created. If the <from_branch> does not exist in the local repository, this command does nothing.

This command can also save remote repositories, in two different ways:

  • If --set-default is specified, the remote destination becomes the default destination, and it is not necessary to give it a name. Subsequent pushes to the same address can be done by simply running pijul push.

    It is not necessary to give a name (using --set-remote) to the default destination, although it is possible.

  • If --set-remote is specified, the remote destination is saved as <set-remote>, and can be reused in subsequent commands. For example, after calling pijul push me@nest.pijul.com:example --set-remote nest, pushing to the same address (me@nest.pijul.com:example) again can be done by running pijul push nest.

Saved remote repositories are written to ".pijul/meta.toml" (at the root of the repository).

Examples

pijul push me@nest.pijul.com:example --set-remote nest
pijul push nest

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --from-branch <from-branch>

    Push patches from branch <from-branch> in the local repository, instead of the current branch (the default).

  • -p <port>, --port <port>

    If the remote repository is accessed over SSH, the port of the remote SSH server, defaulting to 22 (the standard SSH port).

  • --to-branch <to-branch>

    Push patches to branch <to-branch> in the remote repository, instead of the default branch ("master").

  • --set-remote <set-remote>

    Save this remote repository as <set-remote>. In subsequent commands, name <set-remote> can be used instead of this URI.

  • --set-default

    Make this remote repository the default. If subsequent commands involving remote repositories are called without a URI, the default remote repository will be used as the URI.

  • -a, --all

    Don't select patches to push interactively, push all patches instead.

  • [remote]

    Name or URI of the remote repository. A name can be for instance "nest", while a URI can be a HTTP or HTTPS URL, or an SSH address (such as "me@nest.pijul.com:repository"), or a local path.

pijul record

Record the changes in the working copy, creating a patch

Usage

pijul record [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [(-A | --author) <author>] [(-m | --message) <message>] [--depends-on <depends-on>…] [-a | --all] [prefix]…

Description

Record is the only command for creating patches in Pijul.

This command compares the pristine with the working copy, creates a patch, applies it to the pristine, and synchronises file moves.

A patch is then saved to .pijul/patches (from the root of the repository), and can later be exchanged with push and pull.

By default, the comparison happens between the whole pristine and the whole working copy. It can also be restricted to a list of paths, specified as [prefix].

If this is the first command to be run after installing Pijul for the first time, a file named ~/.pijulconfig/config.toml will be created, and your author name will be saved there.

When using Pijul, it is highly recommended to sign your patches. Record automatically does this after keys have been setup.

Interative use

When pijul record is invoked interactively, the user will be asked a series of questions with the prompt [ynkad]. The possible answers are:

  • y: include the change in the patch.
  • n: don't include the change in the patch.
  • k: undo the previous answer and go back to the previous change.
  • a: include all the following changes in the patch.
  • d: do not include any of the following changes in the patch.

Examples

pijul record -a -m "My first patch" -A "me <me@example.com>" src

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Compare branch <branch>, instead of the current branch, with the working copy.

  • -A, --author

    Set the patch's author. By default, the first patch you create on any repository after installing Pijul saves your author name in "~/.pijulconfig/config.toml".

  • -m, --message

    Add a message to this patch, summarising your changes.

  • --depends-on <depends-on>

    Add a list of dependencies to this patch. Dependencies are normally inferred by Pijul, but some semantic dependencies cannot be inferred. Use this command to add them.

  • -a, --all

    Don't select patches to push interactively, push all patches instead.

  • [prefix]…

    Restrict the comparison to a list of paths instead of comparing the whole repository.

pijul remove

Stop tracking a file (does not delete the file from the filesystem).

Usage

pijul remove [-h | --help] [-V | --version] [--repository <repository>] <files>…

Description

Removes files from the tree. This does not remove files from the local filesystem. However, a patch removing a file will delete it from remote repositories when pushing or pulling.

Example

pijul mv a b

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • <files>…

    List of files to remove from the tree.

pijul revert

Reset the working directory to the state of the pristine.

Usage

pijul revert [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [-a | --all] [<prefix>…]

Description

Reset the working directory to the state of the pristine. If there were unrecorded changes (i.e. if the pristine is different from the working directory), ask interactively what unrecorded changes to keep.

Interative use

When pijul revert is invoked interactively, the user will be asked a series of questions with the prompt [ynkad]. The possible answers are:

  • y: revert the current change.
  • n: don't revert the change.
  • k: undo the previous answer and go back to the previous change.
  • a: revert all the following changes.
  • d: do not revert any of the following changes.

Example

Reset directory "src" to its recorded state:

pijul revert src

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Reset to branch <branch> instead of the current branch (the default).

  • <prefix>…

    Reset only that list of paths. If empty, reset the whole directory.

pijul show-dependencies

Output dependencies between patches in dot.

Usage

pijul show-dependencies [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [--depth <depth>] [<hash>…]

Description

Print dependencies between patches in the dot graph description format, on the standard output.

If a list of patch hashes is given as <hash>…, only their dependencies are printed. Else, the dependencies of all patches are printed.

By default, only direct dependencies are output. This can be changed by running this command with a non-zero <depth>. The depth does not count the patch itself, i.e. depth 1 means that only direct dependencies are output.

Example

With graphviz installed, one can turn the output into a PDF:

pijul show-dependencies | dot -Tpdf -o dependencies.pdf

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Reset to branch <branch> instead of the current branch (the default).

  • --depth <depth>

    Recursion depth when traversing the dependency graph. This parameter does not include the patch itself, i.e. depth 1 will show the patch and its direct dependencies.

  • <hash>…

    Show dependencies for that list of patches only. If missing, show the dependencies of all patches in the repository.

pijul status

Show information about this repository: current branch, conflicts…

Usage

pijul status [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>]

Description

Show information about his repository, including:

  • current branch
  • untracked files
  • conflicts
  • unrecorded changes

Example

pijul status

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Show conflicts and unrecorded changes with respect to branch <branch> instead of the current branch.

pijul unrecord

Unapplies a patch from a branch.

Usage

pijul unrecord [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] <patch>…

Description

Unapplies a patch from a branch of the pristine (defaulting to the current branch if --branch is absent). After calling this command, running pijul record on the same branch will ask to create the same patch again.

This is an essential tool to undo mistakes, and has a variety of other uses. It is used internally to merge remote patches with unrecorded changes when pulling and pushing, for instance.

Example

pijul unrecord AfcVxwCRgKbf-aCS0wOuXAekoRNl9xIGM-_0A_KzgZPEZ_55-5klp15WWTjFRtLVDlacWDAas11yrROdlyU11i8

Options

  • -h, --help

    Print a help message and exit.

  • -V, --version

    Print the version of Pijul and exit.

  • --repository <repository>

    Don't run this command in the current directory, run it in <repository> instead.

  • --branch <branch>

    Unrecord on branch <branch> instead of the current branch (the default).

  • <patch>…

    A list of patch hashes to unrecord. Pijul will ask interactively which patches to unrecord if and only if this list is empty.