Monotone Documentation
Monotone is a distributed version control tool. It can help automate many tedious and error-prone tasks in group software development.
Please be aware that monotone is a slightly unorthodox version control tool, and many of its concepts are slightly similar — but significantly different — from concepts with similar names in other version control tools.
Complete table of contents
This chapter should familiarize you with the concepts, terminology, and behavior described in the remainder of the user manual. Please take a moment to read it, as later sections will assume familiarity with these terms.
Suppose you wish to modify a file file.txt on your computer. You begin with one version of the file, load it into an editor, make some changes, and save the file again. Doing so produces a new version of the file. We will say that the older version of the file was a parent, and the new version is a child, and that you have performed an edit between the parent and the child. We may draw the relationship between parent and child using a graph, where the arrow in the graph indicates the direction of the edit, from parent to child.

We may want to identify the parent and the child precisely, for sake of reference. To do so, we will compute a cryptographic hash function, called sha1, of each version. The details of this function are beyond the scope of this document; in summary, the sha1 function takes a version of a file and produces a short string of 20 bytes, which we will use to uniquely identify the version1. Now our graph does not refer to some “abstract” parent and child, but rather to the exact edit we performed between a specific parent and a specific child.

When dealing with versions of files, we will dispense with writing out “file names”, and identify versions purely by their sha1 value, which we will also refer to as their file ID. Using IDs alone will often help us accommodate the fact that people often wish to call files by different names. So now our graph of parent and child is just a relationship between two versions, only identified by ID.

Version control systems, such as monotone, are principally concerned with the storage and management of multiple versions of some files. One way to store multiple versions of a file is, literally, to save a separate complete copy of the file, every time you make a change. When necessary, monotone will save complete copies of your files in their, compressed with the zlib compression format.

Often we find that successive versions of a file are very similar to one another, so storing multiple complete copies is a waste of space. In these cases, rather than store complete copies of each version of a file, we store a compact description of only the changes which are made between versions. Such a description of changes is called a delta.
Storing deltas between files is, practically speaking, as good as storing complete versions of files. It lets you undo changes from a new version, by applying the delta backwards, and lets your friends change their old version of the file into the new version, by applying the delta forwards. Deltas are usually smaller than full files, so when possible monotone stores deltas, using a modified xdelta format. The details of this format are beyond the scope of this document.

After you have made many different files, you may wish to capture a “snapshot” of the versions of all the files in a particular collection. Since files are typically collected into trees in a file system, we say that you want to capture a version of your tree. Doing so will permit you to undo changes to multiple files at once, or send your friend a set of changes to many files at once.
To make a snapshot of a tree, we begin by writing a special file called a manifest. In fact, monotone will write this file for us, but we could write it ourselves too. It is just a plain text file. Each line of a manifest file contains two columns. In the first column we write the ID of a file in your tree, and in the second column we write the path to the file, from the root of our tree to the filename.

Now we note that a manifest is itself a file. Therefore a manifest can serve as input to the sha1 function, and thus every manifest has an ID of its own. By calculating the sha1 value of a manifest, we capture the state of our tree in a single manifest ID. In other words, the ID of the manifest essentially captures all the IDs and file names of every file in our tree, combined. So we may treat manifests and their IDs as snapshots of a tree of files, though lacking the actual contents of the files themselves.

As with versions of files, we may decide to store manifests in their entirety, or else we may store only a compact description of changes which occur between different versions of manifests. As with files, when possible monotone stores compact descriptions of changes between manifests; when necessary it stores complete versions of manifests.
Suppose you sit down to edit some files. Before you start working, you may record a manifest of the files, for reference sake. When you finish working, you may record another manifest. These “before and after” snapshots of the tree of files you worked on can serve as historical records of the set of changes, or changeset, that you made. In order to capture a “complete” view of history – both the changes made and the state of your file tree on either side of those changes – monotone builds a special composite file called a revision each time you make changes. Like manifests, revisions are ordinary text files which can be passed through the sha1 function and thus assigned a revision ID.

The content of a revision makes reference to file IDs, in describing a changeset, and manifest IDs, in describing tree states “before and after” the changeset. Crucially, revisions also make reference to other revision IDs. This fact – that revisions include the IDs of other revisions – causes the set of revisions to join together into a historical chain of events, somewhat like a “linked list”. Each revision in the chain has a unique ID, which includes by reference all the revisions preceeding it. Even if you undo a changeset, and return to a previously-visited manifest ID during the course of your edits, each revision will incorporate the ID of its predecessor, thus forming a new unique ID for each point in history.

Often, you will wish to make a statement about a revision, such as stating the reason that you made some changes, or stating the time at which you made the changes, or stating that the revision passes a test suite. Statements such as these can be thought of, generally, as a bundle of information with three parts:
For example, if you want to say that a particular revision was composed on April 4, 2003, you might make a statement like this:

In an ideal world, these are all the parts of a statement we would need in order to go about our work. In the real world, however, there are sometimes malicious people who would make false or misleading statements; so we need a way to verify that a particular person made a particular statement about a revision. We therefore will add two more pieces of information to our bundle:
When these 2 items accompany a statement, we call the total bundle of 5 items a certificate, or cert. A cert makes a statement in a secure fashion. The security of the signature in a cert is derived from the rsa cryptography system, the details of which are beyond the scope of this document.

Monotone uses certs extensively. Any “extra” information which needs to be stored, transmitted or retrieved — above and beyond files, manifests, and revisions — is kept in the form of certs. This includes change logs, time and date records, branch membership, authorship, test results, and more. When monotone makes a decision about storing, transmitting, or extracting files, manifests, or revisions, the decision is often based on certs it has seen, and the trustworthiness you assign to those certs.
The rsa cryptography system — and therefore monotone itself — requires that you exchange special “public” numbers with your friends, before they will trust certificates signed by you. These numbers are called public keys. Giving someone your public key does not give them the power to impersonate you, only to verify signatures made by you. Exchanging public keys should be done over a trusted medium, in person, or via a trusted third party. Advanced secure key exchange techniques are beyond the scope of this document.
Most of monotone's certs refer to revision IDs. Some certs may refer to file IDs or manifest IDs, depending on context. This capability may also be removed in the future, at which point certs will only refer to revisions.
Monotone moves information in and out of three different types of storage:
All information passes through your local database, en route to some other destination. For example, when changes are made in a working copy, you may save those changes to your database, and later you may synchronize your database with someone else's. Monotone will not move information directly between a working copy and a remote database, or between working copies. Your local database is always the “switching point” for communication.

A working copy is a tree of files in your file system, arranged according to the list of file paths and IDs in a particular manifest. A special directory called MT exists in the root of any working copy. Monotone keeps some special files in the MT directory, in order to track changes you make to your working copy.
Aside from the special MT directory, a working copy is just a normal tree of files. You can directly edit the files in a working copy using a plain text editor or other program; monotone will automatically notice when you make any changes. If you wish to add files, remove files, or move files within your working copy, you must tell monotone explicitly what you are doing, as these actions cannot be deduced.
If you do not yet have a working copy, you can check out a working copy from a database, or construct one from scratch and add it into a database. As you work, you will occasionally commit changes you have made in a working copy to a database, and update a working copy to receive changes that have arrived in a database. Committing and updating take place purely between a database and a working copy; the network is not involved.

A database is a single, regular file. You can copy or back it up using standard methods. Typically you keep a database in your home directory. Databases are portable between different machine types. If a database grows too big, you may choose to remove information from it. You can have multiple databases and divide your work between them, or keep everything in a single database if you prefer. You can dump portions of your database out as text, and read them back into other databases, or send them to your friends.
A database contains many files, manifests, revisions, and certificates, some of which are not immediately of interest, some of which may be unwanted or even false. It is a collection of information received from network servers, working copies, and other databases. You can inspect and modify your databases without affecting your working copies, and vice-versa.
Monotone knows how to exchange information in your database with other remote databases, using an interactive protocol called netsync. It supports three modes of exchange: pushing, pulling, and synchronizing. A pull operation copies data from a remote database to your local database. A push operation copies data from your local database to a remote database. A sync operation copies data both directions. In each case, only the data missing from the destination is copied. The netsync protocol calculates the data to send “on the fly” by exchanging partial hash values of each database.

In general, work flow with monotone involves 3 distinct stages:
The last stage of workflow is worth clarifying: monotone does not blindly apply all changes it receives from a remote database to your working copy. Doing so would be very dangerous, because remote databases are not always trustworthy systems. Rather, monotone evaluates the certificates it has received along with the changes, and decides which particular changes are safe and desirable to apply to your working copy.
You can always adjust the criteria monotone uses to judge the trustworthiness and desirability of changes in your database. But keep in mind that it always uses some criteria; receiving changes from a remote server is a different activity than applying changes to a working copy. Sometimes you may receive changes which monotone judges to be untrusted or bad; such changes may stay in your database but will not be applied to your working copy.
Remote databases, in other words, are just untrusted “buckets” of data, which you can trade with promiscuously. There is no trust implied in communication.
So far we have been talking about revisions as though each logically follows exactly one revision before it, in a simple sequence of revisions.

This is a rosy picture, but sometimes it does not work out this way. Sometimes when you make new revisions, other people are simultaneously making new revisions as well, and their revisions might be derived from the same parent as yours, or contain different changesets. Without loss of generality, we will assume simultaneous edits only happen two-at-a-time; in fact many more edits may happen at once but our reasoning will be the same.
We call this situation of simultaneous edits a fork, and will refer to the two children of a fork as the left child and right child. In a large collection of revisions with many people editing files, especially on many different computers spread all around the world, forks are a common occurrence.

If we analyze the changes in each child revision, we will often find that the changeset between the parent and the left child are unrelated to the changeset between the parent and the right child. When this happens, we can usually merge the fork, producing a common grandchild revision which contains both changesets.

Sometimes, people intentionally produce forks which are not supposed to be merged; perhaps they have agreed to work independently for a time, or wish to change their files in ways which are not logically compatible with each other. When someone produces a fork which is supposed to last for a while (or perhaps permanently) we say that the fork has produced a new branch. Branches tell monotone which revisions you would like to merge, and which you would like to keep separate.
You can see all the available branches using monotone list branches.
Branches are indicated with certs. The cert name branch is
reserved for use by monotone, for the purpose of identifying the
revisions which are members of a branch. A branch cert has a
symbolic “branch name” as its value. When we refer to “a branch”,
we mean all revisions with a common branch name in their branch
certs.
For example, suppose you are working on a program called “wobbler”.
You might develop many revisions of wobbler and then decide to split
your revisions into a “stable branch” and an “unstable branch”, to
help organize your work. In this case, you might call the new branches
“wobbler-stable” and “wobbler-unstable”. From then on, all
revisions in the stable branch would get a cert with name branch
and value wobbler-stable; all revisions in the unstable branch
would get a cert with name branch and value
wobbler-unstable. When a wobbler-stable revision forks,
the children of the fork will be merged. When a
wobbler-unstable revision forks, the children of the fork will
be merged. However, the wobbler-stable and
wobbler-unstable branches will not be merged together, despite
having a common ancestor.

For each branch, the set of revisions with no children is called the heads of the branch. Monotone can automatically locate, and attempt to merge, the heads of a branch. If it fails to automatically merge the heads, it may ask you for assistance or else fail cleanly, leaving the branch alone.
For example, if a fork's left child has a child of its own (a “left grandchild”), monotone will merge the fork's right child with the left grandchild, since those revisions are the heads of the branch. It will not merge the left child with the right child, because the left child is not a member of the heads.

When there is only one revision in the heads of a branch, we say that the heads are merged, or more generally that the branch is merged, since the heads is the logical set of candidates for any merging activity. If there are two or more revisions in the heads of a branch, and you ask to merge the branch, monotone will merge them two-at-a-time until there is only one.
The branch names used in the above section are fine for an example, but they would be bad to use in a real project. The reason is, monotone branch names must be globally unique, over all branches in the world. Otherwise, bad things can happen. Fortunately, we have a handy source of globally unique names — the DNS system.
When naming a branch, always prepend the reversed name of a host that
you control or are otherwise authorized to use. For example, monotone
development happens on the branch net.venge.monotone, because
venge.net belongs to monotone's primary author. The idea is that
this way, you can coordinate with other people using a host to make sure
there are no conflicts — in the example, monotone's primary author can
be certain that no-one else using venge.net will start up a
different program named monotone. If you work for Yoyodyne,
Inc. (owners of yoyodyne.com), then all your branch names should look
like com.yoyodyne.something.
What the something part looks like is up to you, but
usually the first part is the project name (the monotone in
net.venge.monotone), and then possibly more stuff after that to
describe a particular branch. For example, monotone's win32 support
was initially developed on the branch net.venge.monotone.win32.
(For more information, see Naming Conventions.)
This chapter illustrates the basic uses of monotone by means of an example, fictional software project. Before we walk through the tutorial, there are two minor issues to address: standard options and revision selectors.
Before operating monotone, two important command-line options should be explained.
Monotone will cache the settings for these options in your working copy, so ordinarily once you have checked out a project, you will not need to specify them again. We will therefore only mention these arguments in the first example.
Many commands require you to supply 40-character sha1 values as arguments, which identify revisions. These “revision IDs” are tedious to type, so monotone permits you to supply “revision selectors” rather than complete revision IDs. Selectors are a more “human friendly” way of specifying revisions by combining certificate values into unique identifiers. This “selector” mechanism can be used anywhere a revision ID would normally be used. For details on selector syntax, see Selectors.
We are now ready to explore our fictional project.
Our fictional project involves 3 programmers cooperating to write firmware for a robot, the JuiceBot 7, which dispenses fruit juice. The programmers are named Jim, Abe and Beth.
In our example the programmers work privately on laptops, and are usually disconnected from the network. They share no storage system. Thus when each programmer enters a command, it affects only his or her own computer, unless otherwise stated.
In the following, our fictional project team will work through several version control tasks. Some tasks must be done by each member of our example team; other tasks involve only one member.
The first step Jim, Abe and Beth each need to perform is to create a
new database. This is done with the monotone db init command,
providing a --db option to specify the location of the new
database. Each programmer creates their own database, which will
reside in their home directory and store all the revisions, files and
manifests they work on. Monotone requires this step as an explicit
command, to prevent spurious creation of databases when an invalid
--db option is given.
In real life, most people prefer to keep one database for each project
they work on. If we followed that convention here in the tutorial,
though, then all the databases would be called juicebot.db, and
that would make things more confusing to read. So instead, we'll have
them each name their database after themselves.
Thus Jim issues the command:
$ monotone db init --db=~/jim.db
Abe issues the command:
$ monotone db init --db=~/abe.db
And Beth issues the command:
$ monotone db init --db=~/beth.db
Now Jim, Abe and Beth must each generate an rsa key pair for themselves. This step requires choosing a key identifier. Typical key identifiers are similar to email addresses, possibly modified with some prefix or suffix to distinguish multiple keys held by the same owner. Our example programmers will use their email addresses at the fictional “juicebot.co.jp” domain name. When we ask for a key to be generated, monotone will ask us for a passphrase. This phrase is used to encrypt the key when storing it on disk, as a security measure.
Jim does the following:
$ monotone --db=~/jim.db genkey jim@juicebot.co.jp
enter passphrase for key ID [jim@juicebot.co.jp] : <Jim enters his passphrase>
monotone: generating key-pair 'jim@juicebot.co.jp'
monotone: storing key-pair 'jim@juicebot.co.jp' in database
Abe does something similar:
$ monotone --db=~/abe.db genkey abe@juicebot.co.jp
enter passphrase for key ID [abe@juicebot.co.jp] : <Abe enters his passphrase>
monotone: generating key-pair 'abe@juicebot.co.jp'
monotone: storing key-pair 'abe@juicebot.co.jp' in database
as does Beth:
$ monotone --db=~/beth.db genkey beth@juicebot.co.jp
enter passphrase for key ID [beth@juicebot.co.jp] : <Beth enters her passphrase>
monotone: generating key-pair 'beth@juicebot.co.jp'
monotone: storing key-pair 'beth@juicebot.co.jp' in database
Each programmer has now generated a key pair and placed it in their local database. Each can list the keys in their database, to ensure the correct key was generated. For example, Jim might see this:
$ monotone --db=~/jim.db list keys
[public keys]
9e9e9ef1d515ad58bfaa5cf282b4a872d8fda00c jim@juicebot.co.jp
[private keys]
771ace046c27770a99e5fddfa99c9247260b5401 jim@juicebot.co.jp
The hexadecimal string printed out before each key name is a fingerprint of the key, and can be used to verify that the key you have stored under a given name is the one you intended to store. Monotone will never permit one database to store two keys with the same name or the same fingerprint.
This output shows one private and one public key stored under the name
jim@juicebot.co.jp, so it indicates that Jim's key-pair has
been successfully generated and stored. On subsequent commands, Jim
will need to re-enter our passphrase in order to perform
security-sensitive tasks. To simplify matters, Jim decides to store
his security passphrase in his .monotonerc file, by writing a
hook function which returns the passphrase, so that he does not
need to repeatedly be prompted for it:
$ cat >>~/.monotonerc
function get_passphrase(keypair_id)
return "jimsekret"
end
^D
Note that we are appending the new hook to the (possibly existing) file.
We do this to avoid loosing other changes by mistake; therefore, be sure
to check that no other get_passphrase function appears in the
configuration file.
Abe and Beth do the same, with their secret passphrases.
Jim, Abe and Beth all wish to work with one another, and trust one another. For monotone to accept this situation, the team members will need to exchange the public parts of their rsa key with each other.
First, Jim exports his public key:
$ monotone --db=~/jim.db pubkey jim@juicebot.co.jp >~/jim.pubkey
His public key is just a plain block of ASCII text:
$ cat ~/jim.pubkey
[pubkey jim@juicebot.co.jp]
MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCbaVff9SF78FiB/1nUdmjbU/TtPyQqe/fW
CDg7hSg1yY/hWgClXE9FI0bHtjPMIx1kBOig09AkCT7tBXM9z6iGWxTBhSR7D/qsJQGPorOD
DO7xovIHthMbZZ9FnvyB/BCyiibdWgGT0Gtq94OKdvCRNuT59e5v9L4pBkvajb+IzQIBEQ==
[end]
Abe also exports his public key:
$ monotone --db=~/abe.db pubkey abe@juicebot.co.jp >~/abe.pubkey
As does Beth:
$ monotone --db=~/beth.db pubkey beth@juicebot.co.jp >~/beth.pubkey
Then all three team members exchange keys. The keys are not secret, but the team members must be relatively certain that they are communicating with the person they intend to trust, when exchanging keys, and not some malicious person pretending to be a team member. Key exchange may involve sending keys over an encrypted medium, or meeting in person to exchange physical copies, or any number of techniques. All that matters, ultimately, is for each team member to receive the keys of the others.
So eventually, after key exchange, Jim has Beth's and Abe's public key files in his home directory, along with his own. He tells monotone to read the associated key packets into his database:
$ monotone --db=~/jim.db read <~/abe.pubkey
monotone: read 1 packet
$ monotone --db=~/jim.db read <~/beth.pubkey
monotone: read 1 packet
Beth and Abe similarly tell monotone to read read the two new public keys they received into their respective databases.
Before they can begin work on the project, Jim needs to create a
working copy — a directory whose contents monotone will keep track
of. Often, one works on projects that someone else has started, and
creates working copies with the checkout command, which you'll
learn about later. Jim is starting a new project, though, so he does
something a little bit different. He uses the monotone setup
command to create a new working copy.
This command creates the named directory (if it doesn't already exist),
and creates the MT directory within it. The MT directory
is how monotone recognizes that a directory is a working copy, and
monotone stores some bookkeeping files within it. For instance, command
line values for the --db, --branch or --key
options to the setup command will be cached in a file called
MT/options, so you don't have to keep passing them to monotone
all the time.
Jim creates his working copy:
/home/jim$ monotone setup juice
/home/jim$ cd juice
/home/jim/juice$
Notice that Jim has changed his current directory to his newly created working copy. For the rest of this example we will assume that everyone issues all further monotone commands from their working copy directories.
Next Jim decides to add some files to the project. He writes up a file containing the prototypes for the JuiceBot 7:
$ mkdir include
$ cat >include/jb.h
/* Standard JuiceBot hw interface */
#define FLOW_JUICE 0x1
#define POLL_JUICE 0x2
int spoutctl(int port, int cmd, void *x);
/* JuiceBot 7 API */
#define APPLE_SPOUT 0x7e
#define BANANA_SPOUT 0x7f
void dispense_apple_juice ();
void dispense_banana_juice ();
^D
Then adds a couple skeleton source files which he wants Abe and Beth to fill in:
$ mkdir src
$ cat >src/apple.c
#include "jb.h"
void
dispense_apple_juice()
{
/* Fill this in please, Abe. */
}
^D
$ cat >src/banana.c
#include "jb.h"
void
dispense_banana_juice()
{
/* Fill this in please, Beth. */
}
^D
Now Jim tells monotone to add these files to its record of his working copy. He specifies one filename and one directory; monotone recursively scans the directory and adds all its files.
$ monotone --db=~/jim.db add include/jb.h src
monotone: adding include/jb.h to working copy add set
monotone: adding src/apple.c to working copy add set
monotone: adding src/banana.c to working copy add set
This command produces a record of Jim's intentions in a special file called MT/work, stored in the working copy. The file is plain text:
$ cat MT/work
add_file "include/jb.h"
add_file "src/apple.c"
add_file "src/banana.c"
Jim then gets up from his machine to get a coffee. When he returns he has forgotten what he was doing. He asks monotone:
$ monotone --db=jim.db status
new_manifest [2098eddbe833046174de28172a813150a6cbda7b]
old_revision []
old_manifest []
add_file "include/jb.h"
add_file "src/apple.c"
add_file "src/banana.c"
patch "include/jb.h"
from []
to [3b12b2d0b31439bd50976633db1895cff8b19da0]
patch "src/apple.c"
from []
to [2650ffc660dd00a08b659b883b65a060cac7e560]
patch "src/banana.c"
from []
to [e8f147e5b4d5667f3228b7bba1c5c1e639f5db9f]
The output of this command tells Jim that his edits, so far, constitute only the addition of some files. In the output we can see one pecularity of monotone's changeset format. The pecularity is that when monotone records a “new file”, it actually records two separate events: the addition of an empty file to the working copy, and a patch of that file from empty to its intended contents.
Jim wants to see the actual details of the files he added, however, so he runs a command which prints out the status and a GNU “unified diff” of the patches involved in the changeset:
$ monotone --db=jim.db diff
#
# add_file "include/jb.h"
#
# add_file "src/apple.c"
#
# add_file "src/banana.c"
#
# patch "include/jb.h"
# from []
# to [3b12b2d0b31439bd50976633db1895cff8b19da0]
#
# patch "src/apple.c"
# from []
# to [2650ffc660dd00a08b659b883b65a060cac7e560]
#
# patch "src/banana.c"
# from []
# to [e8f147e5b4d5667f3228b7bba1c5c1e639f5db9f]
#
--- include/jb.h
+++ include/jb.h
@ -0,0 +1,13 @
+/* Standard JuiceBot hw interface */
+
+#define FLOW_JUICE 0x1
+#define POLL_JUICE 0x2
+#define SET_INTR 0x3
+int spoutctl(int port, int cmd, void *x);
+
+/* JuiceBot 7 API */
+
+#define APPLE_SPOUT 0x7e
+#define BANANA_SPOUT 0x7f
+void dispense_apple_juice ();
+void dispense_banana_juice ();
--- src/apple.c
+++ src/apple.c
@ -0,0 +1,7 @
+#include "jb.h"
+
+void
+dispense_apple_juice()
+{
+ /* Fill this in please, Abe. */
+}
--- src/banana.c
+++ src/banana.c
@ -0,0 +1,7 @
+#include "jb.h"
+
+void
+dispense_banana_juice()
+{
+ /* Fill this in please, Beth. */
+}
Satisfied with the work he's done, Jim wants to save his changes. He
chooses jp.co.juicebot.jb7 as a branch name. (See Naming Conventions for more information about appropriate branch names.) He
then commits his working copy, which causes monotone to process the
MT/work file and record the file contents, manifest, and revision
into the database.
$ monotone --db=jim.db --branch=jp.co.juicebot.jb7 commit --message='initial checkin of project'
monotone: beginning commit
monotone: manifest 2098eddbe833046174de28172a813150a6cbda7b
monotone: revision 2e24d49a48adf9acf3a1b6391a080008cbef9c21
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 2e24d49a48adf9acf3a1b6391a080008cbef9c21
Monotone did a number of things when committing the new revision. First, we can see from the output that monotone generated a manifest of the tree Jim committed. The manifest is stored inside the database, but Jim can print it out if he wants to see the exact state of all the files referenced by the revision he committed:
$ monotone cat manifest
3b12b2d0b31439bd50976633db1895cff8b19da0 include/jb.h
2650ffc660dd00a08b659b883b65a060cac7e560 src/apple.c
e8f147e5b4d5667f3228b7bba1c5c1e639f5db9f src/banana.c
The column on the left contains cryptographic hashes of the files listed in the column on the right. Such a hash is also called the “file ID” of the file. The file ID identifies the state of each file stored in Jim's tree. The manifest is just a plain text file, identical to the output from the popular sha1sum unix command.
When monotone committed Jim's revision, it also erased the MT/work file, and wrote a new file called MT/revision, which contains the working copy's new base revision ID. Jim can use this revision ID in the future, as an argument to the checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to the new revision. These certs store metadata about the commit. Jim can ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or “out” of a branch in any global sense, nor are any of these cert values true or false in any global sense. Each cert indicates that some person – in this case Jim – would like to associate a revision with some value; it is up to you to decide if you want to accept that association.
Jim can now check the status of his branch using the “heads” command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
Jim now decides he will make his base revision available to his employees. To do this first adds a small amount of extra information to his .monotonerc file, permitting Abe and Beth to access his database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming connections, adjusting his firewall settings as necessary, and runs the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the branch. Therefore Abe can disconnect from the expensive network connection he's on and work locally for a while. When Abe wants to send work back to Jim, or get new work Jim has added, all he needs to do is run the sync command again and work will flow both ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the company. If Jim wants to, he can leave his server running forever, or even put his server on a dedicated computer with better network connectivity. But if Jim is ever unable to play this role of “central server”, perhaps due to a network failure, either Beth or Abe can run the serve command and provide access for the other to sync with. In fact, each employee can run a server if they like, concurrently, to help minimize the risk of service disruption from hardware failures. Changes will flow between servers automatically as clients access them and trade with one another.
In practice, most people like to use at least one central server that is always running; this way, everyone always knows where to go to get the latest changes, and people can push their changes out without first calling their friends and making sure that they have their servers running. To support this style of working, monotone remembers the first server you use, and makes that the default for future operations.
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest directly into Abe's current directory. (If he had specified something other than . at the end, monotone would have created that directory and unpacked the files into it.) Abe then opens up one of the files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets up to answer a phone call, and when he returns to his work he has forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the change log on the command line and the file MT/log is empty because he did not document his changes there. Monotone therefore invokes an external “log message editor” — typically an editor like vi — with an explanation of the changes being committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying “polling implementation of src/apple.c”. He then saves the file and quits the editor. Monotone deletes all the lines beginning with “MT:” and leaves only Abe's short message. Returning to the shell, Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor for her to edit her log message, but this time it fills in the messages she's written so far, and she simply checks them over one last time before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Careful readers will note that, in the previous section, the JuiceBot company's work was perfectly serialized:
The result of this ordering is that Jim's work entirely preceeded Abe's work, which entirely preceeded Beth's work. Moreover, each worker was fully informed of the “up-stream” worker's actions, and produced purely derivative, “down-stream” work:
This is a simple, but sadly unrealistic, ordering of events. In real companies or work groups, people often work in parallel, diverging from commonly known revisions and merging their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any time two revisions diverge from a common parent revision, we say that the revision graph has a fork in it. Forks can happen at any time, and require no coordination between workers. In fact any interleaving of the previous events would work equally well; with one exception: if forks were produced, someone would eventually have to run the merge command, and possibly resolve any conflicts in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim sends out an email saying that the current polling juice dispensers use too much CPU time, and must be rewritten to use the JuiceBot's interrupt system. Beth wakes up first and begins working immediately, basing her work off the revision 80ef9... which is currently in her working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When Abe wants to
send work back to Jim, or get new work Jim has added, all he needs to
do is run the sync command again and work will flow both
ways, bringing each party up to date with the work of the other.
At this point Jim is operating as a sort of “central server” for the
company. If Jim wants to, he can leave his server running forever, or
even put his server on a dedicated computer with better network
connectivity. But if Jim is ever unable to play this role of “central
server”, perhaps due to a network failure, either Beth or Abe can run
the serve command and provide access for the other to
sync with. In fact, each employee can run a server if they
like, concurrently, to help minimize the risk of service disruption from
hardware failures. Changes will flow between servers automatically as
clients access them and trade with one another.
In practice, most people like to use at least one central server that is
always running; this way, everyone always knows where to go to get the
latest changes, and people can push their changes out without first
calling their friends and making sure that they have their servers
running. To support this style of working, monotone remembers the first
server you use, and makes that the default for future operations.
2.9 Making Changes
Abe decides to do some work on his part of the code. He has a copy of
Jim's database contents, but cannot edit any of that data yet. He
begins his editing by checking out the head of the
jp.co.juicebot.jb7 branch into a working copy, so he can edit
it:
$ monotone --db=abe.db --branch=jp.co.juicebot.jb7 checkout .
Monotone unpacks the set of files in the head revision's manifest
directly into Abe's current directory. (If he had specified something
other than . at the end, monotone would have created that
directory and unpacked the files into it.) Abe then opens up one of the
files, src/apple.c, and edits it:
$ vi src/apple.c
<Abe writes some apple-juice dispensing code>
The file src/apple.c has now been changed. Abe gets
up to answer a phone call, and when he returns to his work he has
forgotten what he changed. He can ask monotone for details:
$ monotone diff
#
# patch "src/apple.c"
# from [2650ffc660dd00a08b659b883b65a060cac7e560]
# to [e2c418703c863eabe70f9bde988765406f885fd0]
#
--- src/apple.c
+++ src/apple.c
@ -1,7 +1,10 @
#include "jb.h"
void
dispense_apple_juice()
{
- /* Fill this in please, Abe. */
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 1);
+ while (spoutctl(APPLE_SPOUT, POLL_JUICE, 1) == 0)
+ usleep (1000);
+ spoutctl(APPLE_SPOUT, FLOW_JUICE, 0);
}
Satisfied with his day's work, Abe decides to commit.
$ monotone commit
monotone: beginning commit
monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1
monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979
monotone: branch jp.co.juicebot.jb7
Abe neglected to provide a --message option specifying the
change log on the command line and the file MT/log is empty
because he did not document his changes there. Monotone therefore
invokes an external “log message editor” — typically an editor
like vi — with an explanation of the changes being
committed and the opportunity to enter a log message.
polling implementation of src/apple.c
MT:
MT: ----------------------------------------------------------------------
MT: Enter Log. Lines beginning with `MT:' are removed automatically
MT:
MT: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
MT:
MT: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
MT: old_manifest [2098eddbe833046174de28172a813150a6cbda7b]
MT:
MT: patch "src/apple.c"
MT: from [2650ffc660dd00a08b659b883b65a060cac7e560]
MT: to [e2c418703c863eabe70f9bde988765406f885fd0]
MT:
MT: ----------------------------------------------------------------------
MT:
Abe enters a single line above the explanatory message, saying
“polling implementation of src/apple.c”. He then saves the file and
quits the editor. Monotone deletes all the lines beginning with
“MT:” and leaves only Abe's short message. Returning to the shell,
Abe's commit completes:
monotone: committed revision 70decb4b31a8227a629c0e364495286c5c75f979
Abe then sends his new revision back to Jim:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Beth does a similar sequence. First she syncs her database with
Jim's:
monotone --db=beth.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
She checks out a copy of the tree from her database:
$ monotone --db=beth.db --branch=jp.co.juicebot.jb7 checkout .
She edits the file src/banana.c:
$ vi src/banana.c
<Beth writes some banana-juice dispensing code>
and logs her changes in MT/log right away so she does not
forget what she has done like Abe.
$ vi MT/log
* src/banana.c: Added polling implementation
and logs her changes in MT/log right away so she does not
forget what she has done:
$ vi MT/log
* src/banana.c: Added polling implementation
Later, she commits her work. Monotone again invokes an external editor
for her to edit her log message, but this time it fills in the messages
she's written so far, and she simply checks them over one last time
before finishing her commit:
$ monotone commit
monotone: beginning commit
monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f
monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
monotone: branch jp.co.juicebot.jb7
monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb
And she syncs with Jim again:
$ monotone sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 630] [bytes out: 2844]
monotone: successful exchange with jim-laptop.juicebot.co.jp
2.10 Dealing with a Fork
Careful readers will note that, in the previous section, the JuiceBot
company's work was perfectly serialized:
- Jim did some work
- Abe synced with Jim
- Abe did some work
- Abe synced with Jim
- Beth synced with Jim
- Beth did some work
- Beth synced with Jim
The result of this ordering is that Jim's work entirely preceeded
Abe's work, which entirely preceeded Beth's work. Moreover, each
worker was fully informed of the “up-stream” worker's actions, and
produced purely derivative, “down-stream” work:
- Jim made revision 2e24d...
- Abe changed revision 2e24d... into revision 70dec...
- Beth derived revision 70dec... into revision 80ef9...
This is a simple, but sadly unrealistic, ordering of events. In real
companies or work groups, people often work in parallel,
diverging from commonly known revisions and merging
their work together, sometime after each unit of work is complete.
Monotone supports this diverge/merge style of operation naturally; any
time two revisions diverge from a common parent revision, we say that
the revision graph has a fork in it. Forks can happen at any
time, and require no coordination between workers. In fact any
interleaving of the previous events would work equally well; with one
exception: if forks were produced, someone would eventually have to
run the merge command, and possibly resolve any conflicts
in the fork.
To illustrate this, we return to our workers Beth and Abe. Suppose Jim
sends out an email saying that the current polling juice dispensers
use too much CPU time, and must be rewritten to use the JuiceBot's
interrupt system. Beth wakes up first and begins working immediately,
basing her work off the revision 80ef9... which is currently in her
working copy:
$ vi src/banana.c
<Beth changes her banana-juice dispenser to use interrupts>
Beth finishes and examines her changes:
$ monotone diff
#
# patch "src/banana.c"
# from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
# to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
#
--- src/banana.c
+++ src/banana.c
@ -1,10 +1,15 @
#include "jb.h"
+static void
+shut_off_banana()
+{
+ spoutctl(BANANA_SPOUT, SET_INTR, 0);
+ spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
+}
+
void
-dispense_banana_juice()
+dispense_banana_juice()
{
+ spoutctl(BANANA_SPOUT, SET_INTR, &shut_off_banana);
spoutctl(BANANA_SPOUT, FLOW_JUICE, 1);
- while (spoutctl(NA_SPOUT, FLOWso called the
“file ID” of the file. The file ID identifies the state of each file
stored in Jim's tree. The manifest is just a plain text file,
identical to the output from the popular sha1sum unix
command.
When monotone committed Jim's revision, it also erased the
MT/work file, and wrote a new file called MT/revision,
which contains the working copy's new base revision ID. Jim can use
this revision ID in the future, as an argument to the
checkout command, if he wishes to return to this revision:
$ cat MT/revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21
Finally, monotone also generated a number of certificates, attached to
the new revision. These certs store metadata about the commit. Jim can
ask monotone for a list of certs on this revision.
$ monotone ls certs 2e24d49a48adf9acf3a1b6391a080008cbef9c21
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : branch
Value : jp.co.juicebot.jb7
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : date
Value : 2004-10-26T02:53:08
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : author
Value : jim@juicebot.co.jp
-----------------------------------------------------------------
Key : jim@juicebot.co.jp
Sig : ok
Name : changelog
Value : initial checkin of project
The output of this command has a block for each cert found. Each block
has 4 significant pieces of information. The first indicates the
signer of the cert, in this case jim@juicebot.co.jp. The
second indicates whether this cert is “ok”, meaning whether the
rsa signature provided is correct for the cert data. The third is
the cert name, and the fourth is the cert value. This list shows us
that monotone has confirmed that, according to
jim@juicebot.co.jp, the revision
2e24d49a48adf9acf3a1b6391a080008cbef9c21 is a member of the
branch jp.co.juicebot.jb7, written by
jim@juicebot.co.jp, with the given date and changelog.
It is important to keep in mind that revisions are not “in” or
“out” of a branch in any global sense, nor are any of these cert
values true or false in any global sense. Each cert indicates
that some person – in this case Jim – would like to associate a
revision with some value; it is up to you to decide if you want to
accept that association.
Jim can now check the status of his branch using the “heads”
command, which lists all the head revisions in the branch:
$ monotone heads
branch 'jp.co.juicebot.jb7' is currently merged:
2e24d49a48adf9acf3a1b6391a080008cbef9c21 jim@juicebot.co.jp 2004-10-26T02:53:08
The output of this command tells us that there is only one current
“head” revision in the branch jp.co.juicebot.jb7, and it is
the revision Jim just committed. A head revision is one without any
descendents. Since Jim has not committed any changes to this revision
yet, it has no descendents.
2.8 Network Service
Jim now decides he will make his base revision available to his
employees. To do this first adds a small amount of extra information
to his .monotonerc file, permitting Abe and Beth to access his
database:
$ cat >>~/.monotonerc
function get_netsync_read_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_write_permitted (collection, identity)
if (identity == "abe@juicebot.co.jp") then return true end
if (identity == "beth@juicebot.co.jp") then return true end
return false
end
function get_netsync_anonymous_read_permitted (collection)
return false
end
^D
He then makes sure that his TCP port 5253 is open to incoming
connections, adjusting his firewall settings as necessary, and runs
the monotone serve command:
$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
This command sets up a single listener loop on the host
jim-laptop.juicebot.co.jp, serving the
jp.co.juicebot.jb7 collection. This collection will naturally
include the jp.co.juicebot.jb7 branch, and any sub-branches.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone sync command:
monotone --db=abe.db sync jim-laptop.juicebot.co.jp jp.co.juicebot.jb7
monotone: rebuilding merkle trees for collection jp.co.juicebot.jb7
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
Abe now has, in his database, a copy of everything Jim put in the
branch. Therefore Abe can disconnect from the expensive network
connection he's on and work locally for a while. When A