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.### DependenciesThis 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 CRDTsAccording 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 identifiersVersion 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.
## DiscussionsAnother 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 checkoutSwitch to a branch## Usage```cmdpijul checkout [-h | --help] [-V | --version] [--repository <repository>] [-f | --force] <branch>```## DescriptionReset the working copy to branch `<branch>` of the pristine, and setthat branch as the current branch.## ExampleFrom the repository root:```pijul checkout master```## Options- -f, --forceOnly verify that there are no unrecorded files moves, deletions or additions (ignores unrecorded changes in files). Much faster on large repositories.- -h, --helpPrint a help message and exit.- -V, --versionPrint 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 dependenciesOutput dependencies between patches in dot.## Usage``` cmdpijul dependencies [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [--depth <depth>] [<hash>…]```## DescriptionPrint dependencies between patches in the dot graph descriptionformat, on the standard output.If a list of patch hashes is given as `<hash>…`, only theirdependencies are printed. Else, the dependencies of all patches areprinted.By default, only direct dependencies are output. This can be changedby running this command with a non-zero `<depth>`. The depth does notcount the patch itself, i.e. depth 1 means that only directdependencies are output.## ExampleWith [graphviz](http://www.graphviz.org) installed, one can turn theoutput into a PDF:```pijul dependencies | dot -Tpdf -o dependencies.pdf```## Options- -h, --helpPrint a help message and exit.- -V, --versionPrint 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 parameterdoes not include the patch itself, i.e. depth 1 will show the patchand its direct dependencies.- \<hash\>…Show dependencies for that list of patches only. If missing, showthe dependencies of all patches in the repository.
# pijul distCreates an archive of the repository## Usage```cmdpijul dist [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [-s | --stdout] -d <archive>```## DescriptionCreates 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`.## ExampleThe following produces a file named "pijul-0.8.0.tar.gz":```pijul dist -d pijul-0.8.0```## Options- -h, --helpPrint a help message and exit.- -s, --stdoutOutputs the resulting archive (in binary) to stdout.- -V, --versionPrint 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 helpDisplay help information about Pijul## Usage```cmdpijul help [subcommand]```## DescriptionWith 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 keyManage SSH and signing keys## Usage```cmdpijul key [subcommand]subcommands:genhelpupload```## DescriptionWhen 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.## ExampleGenerate and upload a signing key to to the nest user `me`.```pijul key gen --signing-id me@example.compijul key upload me@nest.pijul.com```## Generate Options- -h, --helpPrints help information- --localSave the keys to the local repository instead of your homedirectory. This might be useful for managing several identities, buthaving several key pairs scattered around your folders is usuallynot 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 thisargument generates an SSH key instead of a signing key.- -V, --versionPrints 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, --portThe port of the remote SSH server when using --upload-to. If notspecified, 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 patchOutput the compressed encoding of a patch (in binary)## Usage```cmdpijul patch [-h | --help] [-V | --version] [--repository <repository>] [--bin] <patch>```## DescriptionOutput 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.## ExamplePrint patch `8BLFYHMVfzEgKcLaumaHwqmn5xk8dAt4w9yUhkJDFh4FYvkXLzxTzcrttj9prZUJiGctWAjZ1VLwURf8e1hkio4Q`:```pijul patch 8BLFYHMVfzEgKcLaumaHwqmn5xk8dAt4w9yUhkJDFh4FYvkXLzxTzcrttj9prZUJiGctWAjZ1VLwURf8e1hkio4Q```## Options- -h, --helpPrint a help message and exit.- -V, --versionPrint 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 pruneDelete a branch from the pristine## Usage```cmdpijul prune [-h | --help] [-V | --version] [--repository <repository>] <branch>```## DescriptionDelete a branch from the pristine. This can only be done if there isat least one other branch (i.e. if there are at least two branchesbefore running this command).## ExampleIf the pristine has two branches, "master" and "unstable":```pijul prune unstable```## Options- -h, --helpPrint a help message and exit.- -V, --versionPrint 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 revertReset the working directory to the state of the pristine.## Usage``` cmdpijul revert [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>] [-a | --all] [<prefix>…]```## DescriptionReset the working directory to the state of the pristine. If therewere unrecorded changes (i.e. if the pristine is *different* from theworking directory), ask interactively what unrecorded changes to keep.## Interactive useWhen `pijul revert` is invoked interactively, the user will be asked aseries 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.## ExampleReset directory "src" to its recorded state:```pijul revert src```## Options- -h, --helpPrint a help message and exit.- -V, --versionPrint 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 statusShow information about this repository: current branch, conflicts…## Usage``` cmdpijul status [-h | --help] [-V | --version] [--repository <repository>] [--branch <branch>]```## DescriptionShow information about his repository, including:- current branch- untracked files- conflicts- unrecorded changes## Example```pijul status```## Options- -h, --helpPrint a help message and exit.- -V, --versionPrint 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 unrecordUnrecords 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 resetReverts 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 removeRemoves 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 remoteManages 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 defaultThis 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- pullWhen set, the default command will only set the default pull remote (and anyother type set in the command flags)- pushWhen set, the default command will only set the default push remote (and anyother type set in the command flags)- nameThe name of the remote to set as the default. The remote must already be savedby pijul.
# pijul remote setAdd or update a remote.## Usage```pijul remote set [-h | --help] [-V | --version] <name> <remote>```## Options- nameShort name to identify the remote- remoteURL to the remote repository
# pijul recordCreates 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 pushPushes 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 pullPulls 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 mvMoves 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 lsLists files tracked by pijul## Usage```pijul ls [OPTIONS]```## Flags`-h, --help Prints help information``-V, --version Prints version information`## Options`--repository <repo-path>`
# pijul logShow 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 initInitializes 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 forkCreate 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 diffShows 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 creditShows 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 cloneClones 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 channelManages 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 changeShows 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 archiveCreates 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 applyApplies 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 addAdds a path to the tree.Pijul has an internal tree to represent the files currently tracked. This command adds files anddirectories 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 thecurrent 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 bashfunction make_md {echo $1mkdir -p $(dirname $1)local FIRST_LINE=truelocal SECTION=""pijul ${@:2} --help | while read; doif [ "$FIRST_LINE" = true ]; thenecho "# $(echo $REPLY | sed -e "s/-/ /g")" > $1elif [[ -z "${REPLY//}" ]]; thenecho >> $1trueelif [ "$REPLY" = "USAGE:" ] || [ "$REPLY" = "ARGS:" ] || [ "$REPLY" = "FLAGS:" ] || [ "$REPLY" = "OPTIONS:" ] || [ "$REPLY" = "SUBCOMMANDS:" ]; thenSECTION=$(echo $REPLY | cut -f 1 -d ":")echo "## " $(echo $SECTION | awk '{print toupper(substr($0, 1, 1))tolower(substr($0, 2))}') >> $1echo >> $1elif [ "$SECTION" = "USAGE" ]; thenif [ "$REPLY" != "" ]; thenecho >> $1echo "\`\`\`" >> $1echo $REPLY >> $1echo "\`\`\`" >> $1echo >> $1fielif [[ "$REPLY" =~ " " ]]; thenecho $REPLY >> $1elif [[ "$REPLY" =~ " " ]]; thenecho "\`$(echo $REPLY | xargs)\`" >> $1echo >> $1elseecho $REPLY >> $1fiFIRST_LINE=falsedonedispatch ${1%.*} ${@:2}}function dispatch {local IS_SUBCOMMAND=falsepijul help ${@:2} | while read i; doif [[ $i = "debug" ]]; thentrueelif [ "$IS_SUBCOMMAND" = true ]; thenCMD=$(echo $i | awk '{print $1}')if [ $CMD != 'help' ]; thenmake_md $1/$CMD.md ${@:2} $CMDfielif [[ $i = "SUBCOMMANDS:" ]]; thenIS_SUBCOMMAND=truefidone}dispatch src/reference
[preprocessor.katex]