This document walks you through the very basics of it. Many of the interactions below may be automated away eventually, but our goal here is to provide an overview of what is going on under the hood.
We are going to assume you have the it executable installed using
cargo install --git https://git.eagain.io/it
Chances are that you already have an SSH key handy. If not, or if you want to use a key specifically for this exercise, generate one using
ssh-keygen -t ed25519
It is also a good idea to add this key to your ssh-agent
, so you don’t have to
type the password every time it is used for signing. Typing ssh-add
usually
does the trick.
Next, we’ll need to teach git to use our SSH key for signing. If you followed above recommendation and are using an agent for signing, the following commands will set it up as a default:
git config --global gpg.format ssh git config --global user.signingKey "key::$(cat /path/to/your_key.pub)"
If you prefer to not mess with your existing git configuration, you can also arrange for the key to be recognised by it itself by running the following command instead:
git config --global it.signingKey "key::$(cat /path/to/your_key.pub)"
Lastly, we’ll create an it identity using this key:
it id init
The command’s output will look similar to this:
{
"committed": {
"repo": "~/.local/share/it/ids",
"ref": "refs/heads/it/ids/671e27d4cce92f747106c7da90bcc2be7072909afa304d008eb8ecbfdebfbfe2",
"commit": "e08c34df95cd28aa212a4d110ecfb8acec2a102c"
},
"data": {
"signed": {
"_type": "eagain.io/it/identity",
"spec_version": "0.1.0",
"prev": null,
"keys": [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDtt6XEdNVInhiKkX+ccN++Bk8kccdP6SeBPg0Aq8XFo"
],
"threshold": 1,
"mirrors": [],
"expires": null,
"custom": {}
},
"signatures": {
"ddc27a697903b8fe3ae3439818af81eaac20ba65e51a4170e3c81eb25abd1767": "5a460b26099ddd42912b7a52ee0c478619425ddfe4a562fd2ffd427d84cde6ab32effd8971308cfcdb64b08ac920e7a2c2a69d11b0ca7fe293e39306cd4d7c01"
}
}
}
The data
object is exactly what is stored in the repository repo
at branch
ref
, which we invite you to convince yourself of using normal git commands.
Identities can describe multiple keys, and carry additional custom metadata, but we’ll skip over this for now.
it is organised around patches. You know, like in the olden days, but not quite. Patches are recorded onto a log dubbed “drop”.
If you have a git repository to toy around with, you can initialise a drop adjacent to the "normal" branches in it. You can record patches (which you may have received from elsewhere) onto that local drop, and push it to wherever you like.
To initialise a drop in this way, just run:
it drop init --description "my project"
This will drop you into $EDITOR
to give you an opportunity to customise the
drop’s metadata, which will look similar to this:
{
"description": "my project",
"roles": {
"drop": {
"ids": [
"671e27d4cce92f747106c7da90bcc2be7072909afa304d008eb8ecbfdebfbfe2"
],
"threshold": 1
},
"snapshot": {
"ids": [
"671e27d4cce92f747106c7da90bcc2be7072909afa304d008eb8ecbfdebfbfe2"
],
"threshold": 1
},
"mirrors": {
"ids": [
"671e27d4cce92f747106c7da90bcc2be7072909afa304d008eb8ecbfdebfbfe2"
],
"threshold": 1
},
"branches": {
"refs/heads/main": {
"ids": [
"671e27d4cce92f747106c7da90bcc2be7072909afa304d008eb8ecbfdebfbfe2"
],
"threshold": 1,
"description": "the default branch"
}
}
},
"custom": {}
}
You may want to check if it has guessed your mainline branch correctly (in the
branches
section), but otherwise just save and exit to finish the
initialisation step. Run
git log -p refs/it/patches
to see the effect.
We want source code patches to be against the refs/heads/main
branch, so we
need to teach the drop about what the current state is:
it merge-point record
Again, you may want to run git log
as above to see what changed. You’ll notice
a line starting with "Re:" in the latest commit message: this is the
topic of a patch, and a merge
point is just a patch with a well-known topic. Run
it topic ls
to see that this topic now exists, and
it topic show c44c20434bfdaa0384b67d48d6c3bb36d755b87576027671f606c404b09d9774
to display the metadata recorded in it.
Whenever you update refs/heads/main
, run merge-point record
again to convey
the new head to the drop. show
ing the topic as above will give you a log of
every such update.
Finally, let’s create a patch: make some changes on a feature branch, like you normally would, and then run
it patch record
This will drop you into $EDITOR
, asking you to describe what the patch is
about. After you save and exit, a new record will be committed onto the drop,
and a new topic will have been created:
$ it topic ls { "topic": "2d2d3c97df62b18d3d1476342fe9d6df0989592f6d55d151350422795da714d8", "subject": "Just testin" } { "topic": "c44c20434bfdaa0384b67d48d6c3bb36d755b87576027671f606c404b09d9774", "subject": "Merges" }
You can post more patches to an existing topic, and reply to a specific entry within the topic. Because a patch in it is really a combination of commentary and source code changes, and source code changes are actually optional, we have a handy shortcut to just, well, comment:
it topic comment record 2d2d3c97df62b18d3d1476342fe9d6df0989592f6d55d151350422795da714d8
Type your comment into $EDITOR
, save and exit. The result may look like this:
$ it topic show 2d2d3c97df62b18d3d1476342fe9d6df0989592f6d55d151350422795da714d8 { "header": { "id": "11337eb409fbd16a034d0323dfa8d879b5a0f36c", "author": { "name": "Kim Altintop", "email": "[email protected]" }, "time": "2023-01-09T09:39:15+01:00", "patch": { "id": "8da0f98009aae98e7ca9df926125aa386a4f6a644c2036e9ec86a0810a7b8a62", "tips": [] }, "in-reply-to": "0c9b7c0b437a3a072f3a1eead17703d22a0bf8f1" }, "message": { "_type": "eagain.io/it/notes/basic", "message": "Ship it" } } { "header": { "id": "0c9b7c0b437a3a072f3a1eead17703d22a0bf8f1", "author": { "name": "Kim Altintop", "email": "[email protected]" }, "time": "2023-01-09T09:23:51+01:00", "patch": { "id": "502b3c4dcf709c9b16df2b58aece9a8966405347a2bf6ccbb305711120984951", "tips": [ "refs/it/bundles/502b3c4dcf709c9b16df2b58aece9a8966405347a2bf6ccbb305711120984951/heads/main" ] } }, "message": { "_type": "eagain.io/it/notes/basic", "message": "Just testin" } }
Notice the patch.tips
array? If the patch contains references which are
conventionally recognised as source code changes (i.e. refs/heads/…
,
refs/tags/…
), their physical location inside the drop’s repository will be
shown here. it is currently lacking a nice UI for this, but you can just do
git diff refs/it/bundles/502b3c4dcf709c9b16df2b58aece9a8966405347a2bf6ccbb305711120984951/heads/main
to see the diff against your currently checked-out branch. If you’re satisfied,
go ahead and merge this ref into your local main
branch. Don’t forget to thank
yourself for the contribution by commenting on the topic!
To wrap it up, you may be wondering how it stored everything in your repository, and perhaps clean it up. Run
git for-each-ref refs/it
to poke around the references it uses to maintain its state. Note, however, that this structure is not part of any public API, and may change without further notice!
The actual patch bundles can be found in
.git/it/bundles
. Note that a patch bundle is self-contained — you can send
them over email, store them in IPFS, or whatever is convenient to move them from
one place to another.
We said that you could receive patches over whatever channel, and apply them to your local drop. A more tangible way is to serve the drop over HTTP, allowing anyone to submit patches to it. While it’s possible to do this from your working repository, it is preferable to create a dedicated repo for the drop:
it drop init --git-dir /the/drop.git --description "my public drop" it merge-point record --git-dir /the/drop.git --source-dir . cd /the/drop.git RUST_LOG=debug it serve
In a second terminal, cd into your working repo and add the drop as a regular git remote:
git remote add dropit /the/drop.git git remote update dropit
You can now submit to it by replacing record
with submit
for the respective
commands, and specifying --drop dropit/patches
to use the remote drop as the
reference.
Currently, an extra command it drop bundles sync
is needed to receive the
patch bundles after updating the remote. This is not particularly smart yet,
especially given that we do support inspecting individual topics (as
opposed to the entire drop history) by it topic unbundle
. We’ll get there.
If you’ve used email to send around patches, or even the excellent
b4 tool, this may all seem
vaguely familiar to you: instead of mbox
archives we have binary git bundles,
what gives?
That’s fair, we haven’t really detailed how it permits much richer interactions and datatypes, for lack of a UI. For brevity, we also haven’t shown that patch bundles can be stored on IPFS, the "commit bit" can be extended to co-maintainers, or how more complex topologies can be created by drop aggregation (and without resorting to HTTP POST).
We invite you to play around with the available commands, read the spec, and perhaps consider to contribute where you see it is currently lacking :)