GLUD3ZVF4LKZKCJJLAQ5DCQ2GDINJFPQ6ZTVPJSDMUKUY5LK2VHQC
As a first approximation, repositories can be thought of as representing a single file by a directed graph $G = (V, E)$ of lines, where each vertex $v\in V$ represented a line, and an edge from $u \in V$ to $v\in V$, labelled by some change (also called patch) number $c$, could be read as "according to change $c$, line $u$ comes before $v$".
This means that changes could introduce vertices and lines, as in the following example, where a line $D$ is introduced between $A$ and $B$:
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/repos-line-add.svg"/>
</div>
Here, the thick line represents the change from the file containing the lines $A$, $B$, $C$ to the file with the new line $D$.
An important feature to note is that **vertices are uniquely identified**, by the hash of the change that introduced them, along with a position in that change. This means that two lines with the same content, introduced by different changes, will be different. It also means that a lines keeps its identity, even if the change is applied in a totally different context.
Moreover, this system is append-only, in the sense that *deletions* are handled by a more sophisticated labelling of the edges. In the example above, if we want to delete line $D$, we just need to make a change mapping the edge introduced by $c_0$ to a deleted edge, which we label by the name $c_1$ of the change that introduces it:
The theory of Pijul is actually quite simple, even though it requires great care in the implementation, in order to get the correct algorithmic complexity.
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/repos-line-del.svg"/>
</div>
From now on, we call the full edges **alive**, and the dashed ones **dead**.
We have just described the two basic kinds of actions in Pijul. There are no other. One kind adds vertices to the graph, along with "alive" edges around them, and the other kind maps an existing edge label onto a different one.
The edge labels are fully described by two parameters: (1) their status (alive, deleted, and a few others related to multiple files and technical details explained below), and (2) the change that introduced them.
### Dependencies
This scheme allows to defines dependencies between changes:
- If a change $c$ adds a vertex, we must have its *"context"*, i.e. the lines before and after it, hence the changes that introduced these lines are in the dependencies of $c$.
- If a change $c$ deletes a vertex, or in other words maps an existing edge introduced by a change $d$, then $c$ must depend on $d$.
Of course, this is just the minimal set of dependencies needed to make sense of the text edits. Hooks and scripts may add extra language-dependent dependencies based on semantics.
### Are edge labels minimal?
Our goals is to find the smallest possible system, both for reasons of mathematical aesthetics (why store useless stuff?) and the other one for performance. Therefore, one immediate question comes to mind: why even keep the change number on the edges?
In order to answer that question, suppose we don't keep the labels, meaning that the maps happen between statuses only. Then, consider the following two situations:
- **Change inverses**
The first issue happens when two authors delete a line in parallel, and one of the authors reverts their change. Applying these changes yields the following diagram, where the two deletions get merged into one, and the inverse applies to both:
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/inverse2.svg"/>
</div>
However, this is not what we expect, since one of the authors explicitly reverted the deletion, while the other performed the same deletion in parallel.
By keeping the labels, this is what we get instead:
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/inverse3.svg"/>
</div>
- **Missing contexts**
For the sake of clarity, in the rest of this post, we name two users Alice (with pronouns "she/her") and Bob (with pronouns "he/his").
This situation, where Alice writes something in the middle of a paragraph $p$, while Bob deletes $p$ in parallel.
One issue here, is that the situation is not symmetric: when Bob applies Alice's change, he can tell immediately that something is wrong, because the context of Alice's edits is labelled as deleted in his repository.
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/known-vertices1.svg"/>
</div>
However, Alice's situation is different: indeed, consider the case where instead of deleting $p$ *in parallel* of her changes, Bob deleted $p$ after applying Alice's change. The edges deleted are exactly the same, but this is not a conflict, as shown in the following diagram:
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/known-vertices2.svg"/>
</div>
The situation is further complicated by the fact that this system doesn't behave symmetrically with the contexts above and below the new line. Indeed, if Bob deleted the *down context* of the line (i.e. if he deleted line $C$) instead of the *up context* (line $B$), Alice could detect the conflict, since in that case, $C$ would have both an alive and a dead edge pointing to it ($C$ is called a "zombie vertex" internally), as shown in the following diagram:
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/known-vertices0.svg"/>
</div>
Keeping the change identifiers on each edge allows us to solve this. Bob adds the labels of all the edges around the deleted lines to the dependencies of his change. Then, Alice can tell whether Bob knows of her change before applying it. The changes are conflict if and only if Bob doesn't know of the new lines.
### Conflicts and CRDTs
According to what we described so far, there are three types of conflicts in Pijul:
- Two alive vertices that do not have a (directed) path between them, in either direction.
- Two alive vertices have paths in opposite directions between them, meaning that the graph has a cycle.
- Or zombie vertices.
In Pijul, **files are generalised to arbitrary graphs of lines**, with different kinds of labels on the edges. In this representation, an edge from line a to line b means that a comes before b in the file.
The global data structure in a repository is *append-only*, in the sense that patches can never delete lines: line deletions from a file are implemented as changes in the labels of the edges pointing to that line.
Moreover, it is easy to show that Pijul implements a **conflict-free replicated datatype** (CRDT): indeed, we're just adding vertices and edges to a graph, or mapping edge labels which we know exist because of dependencies.
The hidden Pijul subcommand `info` (`pijul info --debug`) dumps the entire graph into a file in graphviz' dot format, one for each branch of the repository, called `debug_branch` (usually `debug_master` by default).
However, Pijul's datastructure models, in a conflict-free way, the conflicts that can happen over a text file. In fact, Pijul would remain a CRDT with or without the design choices explained above about edge labels: for example, we could decide that the "deleted" status has higher precedence. But as shown above, that wouldn't model conflicts accurately.
This idea is motivated by a notion of category theory called a [pushout](https://en.wikipedia.org/wiki/Pushout_(category_theory) (see also [the ncat lab](https://ncatlab.org/nlab/show/pushout)).
Moving back to the sequential (i.e. single-user) situation, suppose we start a file with many lines. Our user deletes all of them, adds one new line at the very beginning, and one at the very end, as shown in the following diagram:
A pushout can be seen as a merge between two operations. Originally, many of the ideas behind the theory of Pijul come from [a paper by Mimram and Di Giusto](https://arxiv.org/abs/1311.3903) showing how to generalise flat files to make them have all pushouts. In other words, they show that a close relative of the representation of files in Pijul is the smallest generalisation of standard files that handles all the conflicts, modulo some caveats (that paper doesn't handle line deletions or multiple files).
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/pseudo0.svg"/>
</div>
If you want to learn more about category theory, a good starting point is to try and understand the [Yoneda lemma](https://ncatlab.org/nlab/show/Yoneda+lemma), which roughly shows how to relate any category to operations related to sets. This is important in particular because sets are a well-understood category. A few concepts are required to understand that lemma, mostly [categories](https://ncatlab.org/nlab/show/category+theory), the important example of [the "Set" category](https://ncatlab.org/nlab/show/Set), [functors](https://ncatlab.org/nlab/show/functor) and [natural transformations](https://ncatlab.org/nlab/show/natural+transformation) between them, and finally [presheaves](https://ncatlab.org/nlab/show/presheaf).
If we represented this situation naively like in that diagram, the complexity of applying changes and outputting the repository would depend linearly on the size of the graph, as we would need to traverse the entire thing to know about line $C$, and know it comes after $B$.
The trick is to use what we call "pseudo-edges", which are not part of any change, but are just here to keep the "alive subgraph" (the subgraph of the alive vertices) connected. Every time we delete an edge, we add pseudo-edges between the source of the edge and all the descendants of the target, like the dotted edge in the graph below:
- the graph is acyclic, and contains a path passing through all the vertices.
- for each vertex v, all incoming edges of v have the same label ("alive" vs. "deleted").
- We introduce another type of edge label to indicate that edges represent files, and hence are not be transitive. In particular, when we delete a file, all descendant vertices below must be deleted.
Then the task of presenting the file to the user is relatively straightforward: just go along the path and output all alive vertices. Else, we say the file has a *conflict*, and presenting the state of the file to the user in an intelligible way is not obvious.
- Each file or directory is represented by two separate vertices: one is its name, the other one is an "inode" vertex representing the file itself. This allows directory renames to commute with file renames, and file renames to commute with edits at the beginning of the file. The naive representation where files are represented as just their name would cause the kind of conflicts described above when the contexts of new vertices are deleted in parallel.
The major benefit of this approach, compared to the valid-state-only approach of Git, or to the patches-only approach of Darcs, is that conflicts are just like any normal state. If we remove one side of the conflict (for instance using [unrecord](reference/unrecord.html), the conflict disappears.
If we create a patch that solves a conflict in one context, the same patch will solve the same conflict in any other context. This implies in particular that conflicts do not reappear.
When Alice deletes a block of text, and Bob writes inside that same block in parallel, the merge results in a "deleted context" when Alice tries to apply Bob's patch. On Bob's side, things are a little more complicated, because the situation looks no different than if Alice had been aware of Bob's line when she deleted her lines.
Negating this last sentence yields four different types of conflicts:
In this case, Pijul detects the conflict on both sides, and reconstructs a minimal context of one line around Bob's line, and produces completely equivalent repositories on each side (which is one of the fundamental promises of patch commutation). Because these context lines will now have both alive and deleted incoming edges, they are called "zombies" in Pijul's source code.
- First, the graph of alive files can be disconnected, when a file is introduced in a directory deleted in parallel. When that happens, we double the deleted graph path with a path of pseudo-edges.
- Another kind of conflict is when a file $a$ is renamed in parallel, to $b$ by Alice, and to $c$ by Bob. This includes the case where a file gets moved to two different directories, yielding a non-tree DAG.
- Yet another one is when two different files are given the same name in parallel.
- Finally, one can create a cyclic graph of directories: starting from two directories $a$ and $b$ at the root of the repository, Alice can move $a$ to a subdirectory $b/a$ of $b$, while Bob moves $b$ to a subdirectory $a/b$ of $a$.
However, in order to solve ordering conflicts, we need to add new edges. For complicated reasons related to algorithmic complexity, Pijul chooses to break such an edge in two halves, adding a new vertex in the middle. This also coincidentally yields a simpler patch format, since patches that just add vertices and change edge labels are enough.
We now revisit the first two diagrams in this post: adding and deleting lines. Vertices are now referenced by a change number (for example $c_0$) and a byte interval in that change (for example $[0, n[$, which means "bytes from offset $0$ included to offset $n$ excluded"). Note that vertices can now represent an arbitrary number of lines. Moreover, the size they occupy in memory is independent from $n$ (assuming $n < 2^{64}$).
### Cycles
Starting from a single vertex $c_0:[0, n[$ with $n$ bytes (containing an arbitrary number of lines), introduced by change $c_0$, adding a line is done by first splitting $c_0:[0, n[$ at some offset $i < n$, and inserting the new vertex just like before.
Sometimes, solving a conflict involves ordering lines that were previously unordered. If Alice and Bob have a conflict between two lines A and B, and solve the conflict with opposite orders (i.e. Alice writes AB and Bob BA), this union of their edits will be cyclic, since Alice adds an edge from A to B, and Bob adds an edge from B to A.
This means in particular that the splitting of content into lines is done by the diff algorithm and is encoded in the changes, instead of being set in stone for all repositories. With a different diff algorithm, we could imagine splitting contents according to programming language structure.
Producing a patch is done by comparing an actual file on disk with the output of a generalised file. This is an algorithmically hard problem (actually [NP-complete](https://en.wikipedia.org/wiki/NP-completeness)) when the number of patches involved in the conflict increases. One major source of hardness is that the user might want to change the order between sides of a conflict, another one is that different sides might have a line with the same contents, and we might be forced to choose just one of the sides.
Once we know how to split vertices, deletions are handled almost as before: as shown in the following diagram, where we first apply the same change $c_1$ as in the previous example, and go on to applying change $c_2$, which deletes the end of vertex $c_0:[0, i[$ from some $j<i$ to $i$, and the beginning of vertex $c_1:[0, m[$, from $0$ to some $k<m$.
Pijul solves this by using an [Approximation algorithm](https://en.wikipedia.org/wiki/Approximation_algorithm), with the effect of producing minimal patches only up to a constant factor when solving a conflict, instead of the absolute minimum.
<div style="text-align:center;padding:1em;">
<img src="//pijul.org/img/new-repos-line-del.svg"/>
</div>
One important difference with before is that our previous edges had two different roles which were not clearly distinguishable from one another until now. One of these meanings was to order the lines, and the other one was the status. However, now that vertices can be split, the "status" role of edges becomes ambiguous: for example, a deleted edge pointing to vertex some vertex $c:[i, j[$ means that bytes $[i, j[$ of change $c$ are deleted, but what if we split that vertex into $c:[i, k[$ and $c: [k, j[$? Should we add an extra deleted edge, and if so, where?
There is a simple solution: by introducing a new kind of edge label (named `BLOCK` in the source code) we can distinguish between "internal" or "implicit" edges that are only here to order the blocks, and "status" edges informing about the status of their target vertex. I'll probably explain more about this in a future blog post, or in the manual, or in a paper.
In addition to that difficulty, the are a number of other challenges when implementing the diff algorithm for conflicts: how do we detect that a conflict has been solved? that one side has been deleted, but not the others? that a side has been edited?
## Version identifiers
Version identifiers need more sophistication in Pijul than in other version control systems, because any two patches $c_0$ and $c_1$ that could be produced independently commute, meaning that applying $c_1$ after $c_0$ yields the same repository as applying $c_0$ after $c_1$, and we want version identifiers to reflect that.
This means that meaningful version identifiers must be independent from the order.
While this could be achieved easily with any linear function of the hashes, for example taking the bitwise XOR of hashes, some naive schemes have the problem that servers could trick clients into accepting that versions are synchronised, even though they are not. The bitwise XOR described above has this problem, and so do other linear functions: since the hashes are random, there is a high probability that any $n$ hashes form an independent linear basis of the vector space of hashes.
The problem of forging a version identity becomes easy, as it is just a matter of solving a linear equation.
## Applying and unapplying patches
Our new scheme for version identifiers comes from the discrete log problem. The version identifier of an empty repository is always the identity element $1$, and applying a change with hash $h$ on top of version $V = e^{v}$ is $V^h = e^{v\cdot h}$.
Compared to the previous tasks, this is relatively easy: applying a patch is almost like computing the union of two sets, and patches contain enough information to make that operation reversible.
Now, because getting from $V$ to the exponent $v$ is hard (at least for a classical computer), forging a version identity is hard, since we would need to generate hashes whose multiplication is $v$, without even knowing what $v$ is.
However, in order to get the correct algorithmic complexity, there are a few things to take care of. In particular, when a patch deletes lines, Pijul introduces extra edges to "jump over" deleted parts. Thanks to this trick, the complexity of the outputting algorithm (and therefore also of diff) does not depend on the size of the repository's history.
When unapplying a patch that deleted lines, we have to make sure that these extra edges are deleted, and we might sometimes need to add new ones to connect the newly undeleted lines to other alive parts of the graph, for else they might be skipped by the outputting algorithm.
As pointed by Dan Robertson, this scheme is similar to [homomorphic hashing](https://eprint.iacr.org/2019/227.pdf).
Because of the patch-based nature of Pijul, contribution just means sending a patch to a repository.
This contrasts with snapshot-based systems, where two *versions* have to be present at the same time on the same machine to run heuristics to compare and merge them.
Because of the patch-based nature of Pijul, contribution just means sending a change to a repository.
In practice, contributing to a project on the Nest is as simple as opening a new discussion (using the web interface), and pushing to that discussion. For instance, pushing a patch to discussion 29 of repository `pijul_org/pijul` can be done using:
The owner of a repository can give permissions to other users in the "Admin" tab of the repository. If you have permission to push directly to a repository, you can do so with:
If this is the first time someone pushes a patch to discussion 29, this forks branch "master" of repository `pijul_org/pijul`, and pushes the patches from the local branch onto it.
Where `me` is your own user account. If you are the owner of the repository, you can use the same syntax, or the following shortcut:
```
pijul push me@nest.pijul.com:my-repository
```
To start the discussion from another branch, `--to-branch other-branch:29` can be used instead.
## Discussions
Another way to contribute to a repository on the Nest is to open a discussion, and attach changes to it. A change can be attached with the following syntax:
```
pijul push me@nest.pijul.com:pijul_org/pijul --to-channel main:29
```
Which means *"compare the current channel of my repository with channel `main` on the Nest, and attach the result to discussion number 29"*.
In the special case of channel `main`, one can also do:
```
pijul push me@nest.pijul.com:pijul_org/pijul --to-channel :29
```
# pijul checkout
Switch to a branch
## Usage
```cmd
pijul checkout [-h | --help] [-V | --version] [--repository <repository>] [-f | --force] <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
- -f, --force
Only verify that there are no unrecorded files moves, deletions or additions (ignores unrecorded changes in files). Much faster on large repositories.
- -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 dependencies
Output dependencies between patches in dot.
## Usage
``` cmd
pijul 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](http://www.graphviz.org) installed, one can turn the
output into a PDF:
```
pijul 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 dist
Creates an archive of the repository
## Usage
```cmd
pijul dist [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [-s | --stdout] -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.
- -s, --stdout
Outputs the resulting archive (in binary) to stdout.
- -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 help
Display help information about Pijul
## Usage
```cmd
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 key
Manage SSH and signing keys
## Usage
```cmd
pijul key [subcommand]
subcommands:
gen
help
upload
```
## Description
When called with `gen` it will generate both SSH and signing keys. When called with `upload <address>`, will upload the signing public key to a key server.
## Example
Generate and upload a signing key to to the nest user `me`.
```
pijul key gen --signing-id me@example.com
pijul key upload me@nest.pijul.com
```
## Generate Options
- -h, --help
Prints help information
- --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.
- --signing-id \<email address\>
Generate a signing key for the given email address. Omitting this
argument generates an SSH key instead of a signing key.
- -V, --version
Prints version information
- --for-repository \<repository\>
Don't run this command in the current directory, run it in
\<repository\> instead. This can be used to save local keys.
## Upload Options
- -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\>
Pull the key from \<repository\> instead of the current(CWD) one.
\<address\> normally `me@nest.pijul.com` where `me` is your login name to the nest.
# pijul patch
Output the compressed encoding of a patch (in binary)
## Usage
```cmd
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 patch `8BLFYHMVfzEgKcLaumaHwqmn5xk8dAt4w9yUhkJDFh4FYvkXLzxTzcrttj9prZUJiGctWAjZ1VLwURf8e1hkio4Q`:
```
pijul patch 8BLFYHMVfzEgKcLaumaHwqmn5xk8dAt4w9yUhkJDFh4FYvkXLzxTzcrttj9prZUJiGctWAjZ1VLwURf8e1hkio4Q
```
## 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 prune
Delete a branch from the pristine
## Usage
```cmd
pijul prune [-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 prune 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\>
The name of the branch to be deleted.
# pijul revert
Reset the working directory to the state of the pristine.
## Usage
``` cmd
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.
## Interactive 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 status
Show information about this repository: current branch, conflicts…
## Usage
``` cmd
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
Unrecords all changes upto the given hash
## Usage
```
pijul unrecord [OPTIONS] [change-id]...
```
## Args
`<change-id>... identifier of the change (unambiguous prefixes are accepted)`
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--channel <channel>`
`--repository <repo-path>`
# pijul reset
Reverts the working copy to the given Hash
## Usage
```
pijul reset [FLAGS] [OPTIONS] [files]...
```
## Args
`<files>...`
## Flags
`--dry-run`
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--change <change>`
`--channel <channel>`
`--repository <repo-path>`
# pijul remove
Removes a file from the tree and prestine
## Usage
```
pijul remove [OPTIONS] [paths]...
```
## Args
`<paths>...`
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--repository <repo-path>`
# pijul remote
Manages remote repositories
## Usage
```
pijul remote [OPTIONS] [SUBCOMMAND]
```
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--repository <repo-path>`
## Subcommands
`delete`
`help Prints this message or the help of the given subcommand(s)`
# pijul remote default
This command sets the default remote repositories to use for the `push` and
`pull` commands. This will set the default remote for both `push` and `pull`,
unless changed in the command options.
## Usage
```
pijul remote default [-h | --help] [-V | --version] [--pull] [--push] <name>
```
## Options
- pull
When set, the default command will only set the default pull remote (and any
other type set in the command flags)
- push
When set, the default command will only set the default push remote (and any
other type set in the command flags)
- name
The name of the remote to set as the default. The remote must already be saved
by pijul.
# pijul remote set
Add or update a remote.
## Usage
```
pijul remote set [-h | --help] [-V | --version] <name> <remote>
```
## Options
- name
Short name to identify the remote
- remote
URL to the remote repository
# pijul record
Creates a new change
## Usage
```
pijul record [FLAGS] [OPTIONS] [prefixes]...
```
## Args
`<prefixes>...`
## Flags
`-a, --all`
`-h, --help Prints help information`
-S
`--stdin`
`--tag`
`-V, --version Prints version information`
## Options
--amend <amend>
`--author <author>`
`--channel <channel>`
`-m, --message <message>`
`--repository <repo-path>`
`--timestamp <timestamp>`
# pijul push
Pushes changes to a remote upstream
## Usage
```
pijul push [FLAGS] [OPTIONS] [to] [-- <changes>...]
```
## Args
<to>
`<changes>... Push only these changes`
## Flags
`-a, --all`
`-h, --help Prints help information`
-k Do not check certificates
`-V, --version Prints version information`
## Options
`--channel <channel>`
--path <path>
`--repository <repo-path>`
`--to-channel <to-channel>`
# pijul pull
Pulls changes from a remote upstream
## Usage
```
pijul pull [FLAGS] [OPTIONS] [from] [-- <changes>...]
```
## Args
`<from>`
`<changes>... Pull changes from the local repository, not necessarily from a channel`
## Flags
`-a, --all`
`--full Download full changes, even when not necessary`
`-h, --help Prints help information`
-k Do not check certificates
`-V, --version Prints version information`
## Options
--channel <channel>
`--from-channel <from-channel>`
--path <path>
`--repository <repo-path>`
# pijul mv
Moves a file in the working copy and the tree
## Usage
```
pijul mv [OPTIONS] [paths]...
```
## Args
`<paths>...`
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--repository <repo-path>`
# pijul ls
Lists files tracked by pijul
## Usage
```
pijul ls [OPTIONS]
```
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--repository <repo-path>`
# pijul log
Show the entire log of changes
## Usage
```
pijul log [FLAGS] [OPTIONS]
```
## Flags
`--description`
`--hash-only`
`-h, --help Prints help information`
`--state`
`-V, --version Prints version information`
## Options
`--channel <channel>`
`--repository <repo-path>`
# pijul init
Initializes an empty pijul repository
## Usage
```
pijul init [OPTIONS] [path]
```
## Args
`<path>`
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--channel <channel>`
# pijul fork
Create a new channel
## Usage
```
pijul fork [OPTIONS] <to>
```
## Args
`<to>`
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--change <change>`
`--channel <channel>`
`--repository <repo-path>`
# pijul diff
Shows difference between two channels/changes
## Usage
```
pijul diff [FLAGS] [OPTIONS] [prefixes]...
```
## Args
`<prefixes>...`
## Flags
`-h, --help Prints help information`
`--json`
`--short`
`--tag`
`-V, --version Prints version information`
## Options
`--channel <channel>`
`--repository <repo-path>`
# pijul credit
Shows which patch last affected each line of the every file
## Usage
```
pijul credit [OPTIONS] <file>
```
## Args
`<file>`
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--channel <channel>`
`--repository <repo-path>`
# pijul clone
Clones an existing pijul repository
## Usage
```
pijul clone [FLAGS] [OPTIONS] <remote> [--] [path]
```
## Args
`<remote>`
`<path>`
## Flags
`-h, --help Prints help information`
`--lazy only download changes with alive contents`
-k Do not check certificates
`-V, --version Prints version information`
## Options
--change <change> clone this change and its dependencies
`--channel <channel> set the remote channel [default: main]`
`--path <partial-paths>... clone this path`
--state <state> clone this state
# pijul channel
Manages different channels
## Usage
```
pijul channel [OPTIONS] [SUBCOMMAND]
```
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--repository <repo-path>`
## Subcommands
`delete Delete a channel. The channel must not be the current channel`
`help Prints this message or the help of the given subcommand(s)`
`rename Rename a channel`
`switch Switch to a channel. There must not be unrecorded changes in the working copy`
# pijul change
Shows information about a particular change
## Usage
```
pijul change [OPTIONS] [hash]
```
## Args
`<hash>`
## Flags
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--channel <channel>`
`--repository <repo-path>`
# pijul archive
Creates an archive of the repository
## Usage
```
pijul archive [FLAGS] [OPTIONS] -o <name>
```
## Flags
`-h, --help Prints help information`
-k Do not check certificates
`-V, --version Prints version information`
## Options
`--change <change>...`
`--channel <channel>`
-o <name>
`--prefix <prefix>`
`--remote <remote>`
`--repository <repo-path>`
--state <state>
# pijul apply
Applies one channel to other
## Usage
```
pijul apply [FLAGS] [OPTIONS] [change]
```
## Args
`<change>`
## Flags
`--deps-only`
`-h, --help Prints help information`
`-V, --version Prints version information`
## Options
`--channel <channel>`
`--repository <repo-path>`
# pijul add
Adds a path to the tree.
Pijul has an internal tree to represent the files currently tracked. This command adds files and
directories to that tree.
## Usage
```
pijul add [OPTIONS] [paths]...
```
## Args
`<paths>...`
Paths to add to the internal tree
## Flags
`-h, --help`
Prints help information
`-V, --version`
Prints version information
## Options
`--repository <repo-path>`
Set the repository in which paths must be added. Defaults to the first ancestor of the
current directory that contains a `.pijul` directory.
Longer description.
However, most creative teams don't have everything planned in advance,
and their members often improve each other's code. This might result
However, many teams don't have everything planned in advance,
and their members often edit each other's code. This might result
It is important to note that conflicts in Pijul always happen *between changes*, for example we might say that *"change A conflicts with change B"*. A conflict *resolution* is always a change. One option to fix a conflict between *A* and *B* is to run `pijul unrecord A` (respectively `pijul unrecord B`), which removes *A* (respectively *B*) from history.
- [pijul checkout](./reference/checkout.md)
- [pijul archive](./reference/archive.md)
- [pijul change](./reference/archive.md)
- [pijul channel](./reference/archive.md)
- [pijul channel rename](./reference/channel/rename)
- [pijul channel delete](./reference/channel/delete)
#!/usr/bin/env bash
function make_md {
echo $1
mkdir -p $(dirname $1)
local FIRST_LINE=true
local SECTION=""
pijul ${@:2} --help | while read; do
if [ "$FIRST_LINE" = true ]; then
echo "# $(echo $REPLY | sed -e "s/-/ /g")" > $1
elif [[ -z "${REPLY//}" ]]; then
echo >> $1
true
elif [ "$REPLY" = "USAGE:" ] || [ "$REPLY" = "ARGS:" ] || [ "$REPLY" = "FLAGS:" ] || [ "$REPLY" = "OPTIONS:" ] || [ "$REPLY" = "SUBCOMMANDS:" ]; then
SECTION=$(echo $REPLY | cut -f 1 -d ":")
echo "## " $(echo $SECTION | awk '{print toupper(substr($0, 1, 1))tolower(substr($0, 2))}') >> $1
echo >> $1
elif [ "$SECTION" = "USAGE" ]; then
if [ "$REPLY" != "" ]; then
echo >> $1
echo "\`\`\`" >> $1
echo $REPLY >> $1
echo "\`\`\`" >> $1
echo >> $1
fi
elif [[ "$REPLY" =~ " " ]]; then
echo $REPLY >> $1
elif [[ "$REPLY" =~ " " ]]; then
echo "\`$(echo $REPLY | xargs)\`" >> $1
echo >> $1
else
echo $REPLY >> $1
fi
FIRST_LINE=false
done
dispatch ${1%.*} ${@:2}
}
function dispatch {
local IS_SUBCOMMAND=false
pijul help ${@:2} | while read i; do
if [[ $i = "debug" ]]; then
true
elif [ "$IS_SUBCOMMAND" = true ]; then
CMD=$(echo $i | awk '{print $1}')
if [ $CMD != 'help' ]; then
make_md $1/$CMD.md ${@:2} $CMD
fi
elif [[ $i = "SUBCOMMANDS:" ]]; then
IS_SUBCOMMAND=true
fi
done
}
dispatch src/reference
[preprocessor.katex]