monotone documentation

Top

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 similar — but subtly or significantly different — from concepts with similar names in other version control tools.

Complete table of contents

Table of Contents

1 Concepts

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.

1.1 Versions of files

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.

figures/parent-child.png

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.

figures/parent-child-names-hashes.png

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.

figures/parent-child-hashes.png

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, compressed with the zlib compression format.

figures/three-versions.png

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.

figures/difference-between-versions.png

1.2 Versions of trees

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, in a structured but human-readable format used by several parts of monotone. Each file entry of a manifest binds a specific name, as a full path from the root of the workspace, to a specific file ID, as the hash of its content. In this way, the manifest collects together the snapshot of the file names and contents you have at this point in time; other snapshots with other manifests can use different names for the same file, or different contents for the same name.

Other entries in the manifest format name directories or store file attrs, which we will cover later.

figures/manifest.png

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.

figures/file-id-manifest-id.png

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.

1.3 Historical records

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.

figures/revision.png

The content of a revision includes one or more changesets. These changesets make reference to file IDs, to describe how the tree changed. The revision also contains manifest IDs, as another way of describing the tree “before and after” the changeset — storing this information in two forms allows monotone to detect any bugs or corrupted data before they can enter your history. Finally and 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 preceding 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.

figures/revision-chaining.png

1.4 Certificates

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:

figures/statement.png

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.

figures/cert.png

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.

1.5 Storage and workflow

Monotone moves information in and out of four different types of storage:

The keystore is a directory .monotone/keys in your home directory which contains copies of all your private keys. Each key is stored in a file whose name is the key identifier with some characters converted to underscores. When you use a key to sign a cert, the public half of that key is copied into your local database along with the cert.

All information passes through your local database, en route to some other destination. For example, when changes are made in a workspace, 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 workspace and a remote database, or between workspaces. Your local database is always the “switching point” for communication.

figures/general-workflow.png

A workspace 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 _MTN exists in the root of any workspace. Monotone keeps some special files in the _MTN directory, in order to track changes you make to your workspace. If you ever want to know if a directory is a monotone workspace, just look for this _MTN directory.

Aside from the special _MTN directory, a workspace is just a normal tree of files. You can directly edit the files in a workspace 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 workspace, you must tell monotone explicitly what you are doing, as these actions cannot be deduced.

If you do not yet have a workspace, you can check out a workspace 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 workspace to a database, and update a workspace to receive changes that have arrived in a database. Committing and updating take place purely between a database and a workspace; the network is not involved.

figures/local-workflow.png

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. 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. Underneath, databases are accessed using a standard, robust data manager, which makes using even very large databases efficient. In dire emergencies, you can directly examine and manipulate a database using a simple SQL interface.

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, workspaces, and other databases. You can inspect and modify your databases without affecting your workspaces, 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.

figures/network-workflow.png

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 workspace. 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 workspace.

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 workspace. 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 workspace.

Remote databases, in other words, are just untrusted “buckets” of data, which you can trade with promiscuously. There is no trust implied in communication.

1.6 Forks and merges

So far we have been talking about revisions as though each logically follows exactly one revision before it, in a simple sequence of revisions.

figures/linear-history.png

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.

figures/fork.png

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.

figures/merge.png

1.7 Branches

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 mtn 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.

figures/two-branches.png

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.

figures/branch-heads.png

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.

1.7.1 Branch Names

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.)

2 Tutorial

This chapter illustrates the basic uses of monotone by means of an example, fictional software project.

2.1 Issues

Before we walk through the tutorial, there are two minor issues to address: standard options and revision selectors.

2.1.1 Standard Options

Before operating monotone, two important command-line options should be explained.

Monotone will cache the settings for these options in your workspace, 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.

2.1.2 Revision Selectors

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.

2.2 The 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.

2.3 Creating a Database

The first step Jim, Abe and Beth each need to perform is to create a new database. This is done with the mtn 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.mtn, 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:

     $ mtn db init --db=~/jim.mtn

Abe issues the command:

     $ mtn db init --db=~/abe.mtn

And Beth issues the command:

     $ mtn db init --db=~/beth.mtn

2.4 Generating Keys

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:

     $ mtn genkey jim@juicebot.co.jp
     mtn: generating key-pair 'jim@juicebot.co.jp'
     enter passphrase for key ID [jim@juicebot.co.jp] : <Jim enters his passphrase>
     confirm passphrase for key ID [jim@juicebot.co.jp]: <Jim confirms his passphrase>
     mtn: storing key-pair 'jim@juicebot.co.jp' in /home/jim/.monotone/keys

Abe does something similar:

     $ mtn genkey abe@juicebot.co.jp
     mtn: generating key-pair 'abe@juicebot.co.jp'
     enter passphrase for key ID [abe@juicebot.co.jp] : <Abe enters his passphrase>
     confirm passphrase for key ID [abe@juicebot.co.jp]: <Abe confirms his passphrase>
     mtn: storing key-pair 'abe@juicebot.co.jp' in /home/abe/.monotone/keys

as does Beth:

     $ mtn genkey beth@juicebot.co.jp
     mtn: generating key-pair 'beth@juicebot.co.jp'
     enter passphrase for key ID [beth@juicebot.co.jp] : <Beth enters her passphrase>
     confirm passphrase for key ID [beth@juicebot.co.jp]: <Beth confirms her passphrase>
     mtn: storing key-pair 'beth@juicebot.co.jp' in /home/beth/.monotone/keys

Each programmer has now generated a key pair and placed it in their keystore. Each can list the keys in their keystore, to ensure the correct key was generated. For example, Jim might see this:

     $ mtn list keys
     
     [public keys]
     9e9e9ef1d515ad58bfaa5cf282b4a872d8fda00c jim@juicebot.co.jp   (*)
     (*) - only in /home/jim/.monotone/keys/
     
     
     [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 keystore 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 his passphrase in order to perform security-sensitive tasks. Jim isn't very worried about security (and, more importantly, it simplifies the tutorial text to skip the passphrase prompts) so he decides to store his passphrase in his monotonerc file. He does this by writing a hook function which returns the passphrase:

     $ mkdir ~/.monotone
     $ cat >>~/.monotone/monotonerc
     function get_passphrase(keypair_id)
       return "jimsekret"
     end
     ^D

Now whenever monotone needs his passphrase, it will call this function instead of prompting him to type it. Note that we are appending the new hook to the (possibly existing) file. We do this to avoid losing 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.

2.5 Starting a New Project

Before he can begin work on the project, Jim needs to create a workspace — a directory whose contents monotone will keep track of. Often, one works on projects that someone else has started, and creates workspaces 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 mtn setup command to create a new workspace.

This command creates the named directory (if it doesn't already exist), and creates the _MTN directory within it. The _MTN directory is how monotone recognizes that a directory is a workspace, 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 _MTN/options, so you don't have to keep passing them to monotone all the time.

He chooses jp.co.juicebot.jb7 as a branch name. (See Naming Conventions for more information about appropriate branch names.) Jim then creates his workspace:

     /home/jim$ mtn --db=jim.mtn --branch=jp.co.juicebot.jb7 setup juice
     /home/jim$ cd juice
     /home/jim/juice$

Notice that Jim has changed his current directory to his newly created workspace. For the rest of this example we will assume that everyone issues all further monotone commands from their workspace directories.

2.6 Adding Files

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 workspace. He specifies one filename and one directory; monotone recursively scans the directory and adds all its files.

     $ mtn add include/jb.h src
     mtn: adding include/jb.h to workspace manifest
     mtn: adding src/apple.c to workspace manifest
     mtn: adding src/banana.c to workspace manifest

This command produces a record of Jim's intentions in a special file called _MTN/revision, stored in the workspace. The file is plain text:

     $ cat _MTN/revision
     
     format_version "1"
     
     new_manifest [2098eddbe833046174de28172a813150a6cbda7b]
     
     old_revision []
     
     add_file "include/jb.h"
      content [3b12b2d0b31439bd50976633db1895cff8b19da0]
     
     add_file "src/apple.c"
      content [2650ffc660dd00a08b659b883b65a060cac7e560]
     
     add_file "src/banana.c"
      content [e8f147e5b4d5667f3228b7bba1c5c1e639f5db9f]

You will never have to look at this file, but it is nice to know that it is there.

Jim then gets up from his machine to get a coffee. When he returns he has forgotten what he was doing. He asks monotone:

     $ mtn status
     Current branch: jp.co.juicebot.jb7
     Changes against parent :
       added   include/jb.h
       added   src/apple.c
       added   src/banana.c

The output of this command tells Jim that his edits, so far, constitute only the addition of some files.

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:

     $ mtn diff
     #
     # old_revision []
     #
     # add_file "include/jb.h"
     #  content [3b12b2d0b31439bd50976633db1895cff8b19da0]
     #
     # add_file "src/apple.c"
     #  content [2650ffc660dd00a08b659b883b65a060cac7e560]
     #
     # add_file "src/banana.c"
     #  content [e8f147e5b4d5667f3228b7bba1c5c1e639f5db9f]
     #
     ============================================================================
     --- include/jb.h
     +++ include/jb.h 3b12b2d0b31439bd50976633db1895cff8b19da0
     @ -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 2650ffc660dd00a08b659b883b65a060cac7e560
     @ -0,0 +1,7 @
     +#include "jb.h"
     +
     +void
     +dispense_apple_juice()
     +{
     +  /* Fill this in please, Abe. */
     +}
     ============================================================================
     --- src/banana.c
     +++ src/banana.c e8f147e5b4d5667f3228b7bba1c5c1e639f5db9f
     @ -0,0 +1,7 @
     +#include "jb.h"
     +
     +void
     +dispense_banana_juice()
     +{
     +  /* Fill this in please, Beth. */
     +}

2.7 Committing Work

Satisfied with the work he's done, Jim wants to save his changes. He then commits his workspace, which causes monotone to process the _MTN/revision file and record the file contents, manifest, and revision into the database. Since he provided a branch name when he ran setup, monotone will use this as the default branch name when he commits.

     $ mtn commit --message="initial checkin of project"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 2e24d49a48adf9acf3a1b6391a080008cbef9c21

When monotone committed Jim's revision, it updated _MTN/revision to record the workspace'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:

     $ mtn automate get_base_revision_id
     2e24d49a48adf9acf3a1b6391a080008cbef9c21

Monotone also generated a number of certificates attached to the new revision, and made sure that the database contained a copy of Jim's public key. These certs store metadata about the commit. Jim can ask monotone for a list of certs on this revision.

     $ mtn 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:

     $ mtn 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 Basic Network Service

Jim now decides he will make his base revision available to his employees. To do this, he arranges for Abe and Beth to synchronise their databases with his, over the network. There are two pre-requisites for this: first, he has to get a copy of each of their public keys; then, he has to tell monotone that the holders of those keys are permitted to access his database. Finally, with these pre-requisites in place, he needs to tell monotone to provide network access to his database.

First, Abe exports his public key:

     $ mtn --db=~/abe.mtn pubkey abe@juicebot.co.jp >~/abe.pubkey

His public key is just a plain block of ASCII text:

     $ cat ~/abe.pubkey
     [pubkey abe@juicebot.co.jp]
     MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCbaVff9SF78FiB/1nUdmjbU/TtPyQqe/fW
     CDg7hSg1yY/hWgClXE9FI0bHtjPMIx1kBOig09AkCT7tBXM9z6iGWxTBhSR7D/qsJQGPorOD
     DO7xovIHthMbZZ9FnvyB/BCyiibdWgGT0Gtq94OKdvCRNuT59e5v9L4pBkvajb+IzQIBEQ==
     [end]

Beth also exports her public key:

     $ mtn --db=~/beth.mtn pubkey beth@juicebot.co.jp >~/beth.pubkey

Then Abe and Beth both send their keys to Jim. The keys are not secret, but the team members must be relatively certain that they are exchanging keys with the person they intend to trust, 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 that Jim receives both Abe's and Beth's key in a way that he can be sure of.

So eventually, after key exchange, Jim has the public key files in his home directory. He tells monotone to read the associated key packets into his database:

     $ cat ~/abe.pubkey ~/beth.pubkey | mtn --db=~/jim.mtn read
     mtn: read 2 packets

Now Jim's monotone is able to identify Beth and Abe, and he is ready to give them permission to access his database. He does this by editing a pair of small files in his ~/.monotone directory:

     $ cat >>~/.monotone/read-permissions
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     ^D
     
     $ cat >>~/.monotone/write-permissions
     abe@juicebot.co.jp
     beth@juicebot.co.jp
     ^D

These files are read by the default monotone hooks that will decide whether remote monotone users will be allowed access to Jim's database, identified by the named keys.

Jim then makes sure that his TCP port 4691 is open to incoming connections, adjusting his firewall settings as necessary, and runs the monotone serve command:

     $ mtn --db=jim.mtn serve "jp.co.juicebot.jb7*"

This command starts monotone listening on all network interfaces of his laptop on the default port 4691, serving any branch matching jp.co.juicebot.jb7*. This will naturally include the jp.co.juicebot.jb7 branch, and any sub-branches. The quotes around "jp.co.juicebot.jb7*" are there to protect the * from expansion by the shell; they have no meaning to monotone.

2.9 Synchronising Databases

With Jim's server preparations done, now Abe is ready to fetch Jim's code. To do this he issues the monotone sync command:

     $ mtn --db=abe.mtn sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7*"
     mtn: setting default server to jim-laptop.juicebot.co.jp
     mtn: setting default branch include pattern to 'jp.co.juicebot.jb7*'
     mtn: setting default branch exclude pattern to ''
     mtn: connecting to jim-laptop.juicebot.co.jp
     mtn: first time connecting to server jim-laptop.juicebot.co.jp:4691
     mtn: I'll assume it's really them, but you might want to double-check
     mtn: their key's fingerprint: 9e9e9ef1d515ad58bfaa5cf282b4a872d8fda00c
     mtn: warning: saving public key for jim@juicebot.co.jp to database
     mtn: finding items to synchronize:
     mtn: bytes in | bytes out | revs in | revs out | revs written
     mtn:     2587 |      1025 |       1 |        0 |            1
     mtn: 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. Remember that, in monotone, work is done between workspaces in the filesystem and the local database; network connectivity is necessary only when that work is to be shared with others.

As we follow the juicebot team through the next several steps, we'll see them run the sync command again with Jim, and work will flow both ways. The first time you sync a new database, monotone remembers the server and branch patterns you use, and makes them the default for future operations.

At the end of each exchange, information about all changes in the branch known to each database have been sent to the other party - including the work of the third team member that had previously been exchanged. As well as allowing each team member to learn about the others' work, this also means that each party's laptop contains a backup of the others' work too.

Jim, Abe and Beth will continue working like this while they're getting started, and we'll revisit the issue of network service with them a little later as the project grows.

2.10 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 workspace, so he can edit it:

     $ mtn --db=abe.mtn --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:

     $ mtn diff
     #
     # old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
     #
     # patch "src/apple.c"
     #  from [2650ffc660dd00a08b659b883b65a060cac7e560]
     #    to [e2c418703c863eabe70f9bde988765406f885fd0]
     #
     ============================================================================
     --- src/apple.c 2650ffc660dd00a08b659b883b65a060cac7e560
     +++ src/apple.c e2c418703c863eabe70f9bde988765406f885fd0
     @ -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.

     $ mtn commit
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'

Abe neglected to provide a --message option specifying the change log on the command line and the file _MTN/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
     MTN:
     MTN: ----------------------------------------------------------------------
     MTN: Enter Log.  Lines beginning with `MTN:' are removed automatically
     MTN:
     MTN: format_version "1"
     MTN:
     MTN: new_manifest [b33cb337dccf21d6673f462d677a6010b60699d1]
     MTN:
     MTN: old_revision [2e24d49a48adf9acf3a1b6391a080008cbef9c21]
     MTN:
     MTN: patch "src/apple.c"
     MTN: from [2650ffc660dd00a08b659b883b65a060cac7e560]
     MTN:   to [e2c418703c863eabe70f9bde988765406f885fd0]
     MTN:
     MTN: ----------------------------------------------------------------------
     MTN:

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 “MTN:” and leaves only Abe's short message. Returning to the shell, Abe's commit completes:

     mtn: committed revision 70decb4b31a8227a629c0e364495286c5c75f979

Abe then sends his new revision back to Jim:

     $ mtn sync
     mtn: connecting to jim-laptop.juicebot.co.jp
     mtn: finding items to synchronize:
     mtn:   certs |    keys | revisions
     mtn:       8 |       2 |         2
     mtn: bytes in | bytes out | revs in | revs out | revs written
     mtn:      615 |      2822 |       0 |        1 |            0
     mtn: successful exchange with jim-laptop.juicebot.co.jp

Beth does a similar sequence. First she syncs her database with Jim's:

     $ mtn --db=beth.mtn sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7*"
     mtn: setting default server to jim-laptop.juicebot.co.jp
     mtn: setting default branch include pattern to 'jp.co.juicebot.jb7*'
     mtn: setting default branch exclude pattern to ''
     mtn: connecting to jim-laptop.juicebot.co.jp
     mtn: first time connecting to server jim-laptop.juicebot.co.jp:4691
     mtn: I'll assume it's really them, but you might want to double-check
     mtn: their key's fingerprint: 9e9e9ef1d515ad58bfaa5cf282b4a872d8fda00c
     mtn: warning: saving public key for jim@juicebot.co.jp to database
     mtn: finding items to synchronize:
     mtn: bytes in | bytes out | revs in | revs out | revs written
     mtn:     4601 |      1239 |       2 |        0 |            1
     mtn: verifying new revisions (this may take a while)
     mtn: bytes in | bytes out | revs in | revs out | revs written
     mtn:     4601 |      1285 |       2 |        0 |            2
     mtn: successful exchange with jim-laptop.juicebot.co.jp

She checks out a copy of the tree from her database:

     $ mtn --db=beth.mtn --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 _MTN/log right away so she does not forget what she has done like Abe.

     $ vi _MTN/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:

     $ mtn commit
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb

And she syncs with Jim again:

     $ mtn sync
     mtn: connecting to jim-laptop.juicebot.co.jp
     mtn: finding items to synchronize:
     mtn:   certs |    keys | revisions
     mtn:      12 |       3 |         3
     mtn: bytes in | bytes out | revs in | revs out | revs written
     mtn:      709 |      2879 |       0 |        1 |            0
     mtn: successful exchange with jim-laptop.juicebot.co.jp

2.11 Dealing with a Fork

Careful readers will note that, in the previous section, the JuiceBot company's work was perfectly serialized:

  1. Jim did some work
  2. Abe synced with Jim
  3. Abe did some work
  4. Abe synced with Jim
  5. Beth synced with Jim
  6. Beth did some work
  7. Beth synced with Jim

The result of this ordering is that Jim's work entirely preceded Abe's work, which entirely preceded Beth's work. Moreover, each worker was fully informed of the “up-stream” worker's actions, and produced purely derivative, “down-stream” work:

  1. Jim made revision 2e24d...
  2. Abe changed revision 2e24d... into revision 70dec...
  3. 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 workspace:

     $ vi src/banana.c
     <Beth changes her banana-juice dispenser to use interrupts>

Beth finishes and examines her changes:

     $ mtn diff
     #
     # old_revision [80ef9c9d251d39074d37e72abf4897e0bbae1cfb]
     #
     # patch "src/banana.c"
     #  from [7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a]
     #    to [5e6622cf5c8805bcbd50921ce7db86dad40f2ec6]
     #
     ============================================================================
     --- src/banana.c 7381d6b3adfddaf16dc0fdb05e0f2d1873e3132a
     +++ src/banana.c 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     @ -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(BANANA_SPOUT, POLL_JUICE, 1) == 0)
     -    usleep (1000);
     -  spoutctl(BANANA_SPOUT, FLOW_JUICE, 0);
      }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning commit on branch 'jp.co.juicebot.jb7.muffins'
     mtn: committed revision d33caefd61823ecbb605c39ffb84705dec449857

That's all there is to it — there is now a jp.co.juicebot.jb7.muffins branch, with her initial checkin on it. She can make further checkins from the same workspace, and they will automatically go to the muffins branch; if anyone else wants to help her work on muffins, they can check out that branch as usual.

Of course, while Beth is working on the new muffins code, Abe is still making fixes to the main line. Occasionally, Beth wants to integrate his latest work into the muffins branch, so that her version doesn't fall too far behind. She does this by using the propagate command:

     $ mtn propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins
     mtn: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins
     mtn: [source] da003f115752ac6e4750b89aaca9dbba178ac80c
     mtn: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af
     mtn: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab jim@juicebot.co.jp 2004-10-26T:12:44:23 found
     mtn: trying 3-way merge
     mtn: [merged] 89585b3c5e51a5a75f5d1a05dda859c5b7dde52f

The propagate merges all of the new changes on one branch onto another.

When the muffins code is eventually stable and ready to be integrated into the main line of development, she simply propagates the other way:

     $ mtn propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7
     mtn: propagating jp.co.juicebot.jb7.muffins -> jp.co.juicebot.jb7
     mtn: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
     mtn: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af
     mtn: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 jim@juicebot.co.jp 2004-10-26T:15:25:05 found
     mtn: [merged] 03f7495b51cc70b76872ed019d19dee1b73e89b6

Monotone always records the full history of all merges, and is designed to handle an arbitrarily complicated graph of changes. You can make a branch, then branch off from that branch, propagate changes between arbitrary branches, and so on; monotone will track all of it, and do something sensible for each merge. Of course, it is still probably a good idea to come up with some organization of branches and a plan for which should be merged to which other ones. Monotone may keep track of graphs of arbitrary complexity — but you will have more trouble. Whatever arrangement of branches you come up with, though, monotone should be able to handle it.

2.13 Network Service Revisited

Up until now, Jim has been using his laptop and database as a sort of “central server” for the company; Abe and Beth have been syncing with Jim, and learning of each other's work via Jim's database. This has worked fine while the product has been in early development; Jim has good network connectivity in Japan, and has been staying home concentrating on programming. He has been able to leave his laptop connected and running all the time, while his employees in different time-zones work and sync their databases. This is now starting to change, and two problems are starting to cause occasional difficulties.

The juicebot team are resourceful, and by now quite used to working independently. While Jim has been away travelling, Abe and Beth have come up with their own solution to the first problem: they'll run servers from their databases, setting them up just like Jim did previously. That way, if Jim's database is offline, either Beth or Abe can run the serve command and provide access for the other to sync with. Beth also has the idea to create a second database for the serve process, and to sync her development database with that server locally, avoiding locking contention between multiple monotone processes on the one database file.

When Jim reappears, the next person to sync with him will often pass him information about both employees' work that they've sync'ed with eachother in the meantime, just as he used to do. In fact, Jim now finds it more convenient to initiate the sync with one of the other servers when he has a spare moment and dynamic connectivity from a hotel room or airport. Changes will flow between servers automatically as clients access them and trade with one another.

This gets them by for a while, but there are still occasional inconveniences. Abe and Beth live in very different time-zones, and don't always have reliable network connectivity, so sometimes Jim finds that neither of them is online to sync with when he has the chance. Jim now also has several customers interested in beta-testing the new code, and following changes as the bugs and issues they report are addressed.

Jim decides it's time for a permanent server they can all sync with; 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.

Jim has rented some web server space on a service provider's shared system for the JuiceBot Inc. public website, www.juicebot.co.jp; he thinks this server will be a good place to host the central monotone server too. He sets up a new monotone database on the server, generates a new key specially for the server (so he doesn't have to expose his own development private key on the shared system), and loads in the team-members' keys:

     $ mtn --db=server.mtn db init
     $ mtn genkey monotone-server@www.juicebot.co.jp
     mtn: generating key-pair 'monotone-server@www.juicebot.co.jp'
     enter passphrase for key ID [monotone-server@www.juicebot.co.jp] : <Jim enters a new passphrase>
     confirm passphrase for key ID [monotone-server@www.juicebot.co.jp]: <Jim confirms the passphrase>
     mtn: storing key-pair 'monotone-server@www.juicebot.co.jp' in /home/jim/.monotone/keys
     $ cat abe.pubkey beth.pubkey jim.pubkey | mtn --db=server.mtn read
     mtn: read 3 packets

For the team members, he sets up the permissions files on the server much like before — except that of course he needs to also grant his jim@juicebot.co.jp key permission to access the new server. For the beta-testers, Jim wants to allow them read-only access just to the main JuiceBot 7 development line, but not to any of the sub-branches where other experimental development is going on. He adds some lines at the top of the ~/.monotone/read-permissions on the server, above the broader permissions given to team-members. See the Hook Reference for get_netsync_read_permitted for more details; the resulting file looks like this:

     comment "Provide beta-testers with specific read-only access"
     pattern "jp.co.juicebot.jb7"
     allow "beta1@juicebot.co.jp"
     allow "beta2@juicebot.co.jp"
     continue "true"
     
     comment "Fall-through, and allow staff access to all branches"
     pattern "*"
     allow "abe@juicebot.co.jp"
     allow "beth@juicebot.co.jp"
     allow "jim@juicebot.co.jp"

Jim could log in and start the monotone process manually from his shell # 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(BANANA_SPOUT, POLL_JUICE, 1) == 0) - usleep (1000); - spoutctl(BANANA_SPOUT, FLOW_JUICE, 0); }

She commits her work:

     $ mtn commit --message="interrupt implementation of src/banana.c"
     mtn: beginning commit on branch 'jp.co.juicebot.jb7'
     mtn: committed revision 8b41b5399a564494993063287a737d26ede3dee4

And she syncs with Jim:

     $ mtn sync

Unfortunately, before Beth managed to sync with Jim, Abe had woken up and implemented a similar interrupt-based apple juice dispenser, but his workspace is 70dec..., which is still “upstream” of Beth's.

     $ vi apple.c
     <Abe changes his apple-juice dispenser to use interrupts>

Thus when Abe commits, he unknowingly creates a fork:

     $ mtn commit --message="interrupt implementation of src/apple.c"

Abe does not see the fork yet; Abe has not actually seen any of Beth's work yet, because he has not synchronized with Jim. Since he has new work to contribute, however, he now syncs:

     $ mtn sync

Now Jim and Abe will be aware of the fork. Jim sees it when he sits down at his desk and asks monotone for the current set of heads of the branch:

     $ mtn heads
     mtn: branch 'jp.co.juicebot.jb7' is currently unmerged:
     39969614e5a14316c7ffefc588771f491c709152 abe@juicebot.co.jp 2004-10-26T02:53:16
     8b41b5399a564494993063287a737d26ede3dee4 beth@juicebot.co.jp 2004-10-26T02:53:15

Clearly there are two heads to the branch: it contains an un-merged fork. Beth will not yet know about the fork, but in this case it doesn't matter: anyone can merge the fork, and since there are no conflicts Jim does so himself:

     $ mtn merge
     mtn: starting with revision 1 / 2
     mtn: merging with revision 2 / 2
     mtn: [source] 39969614e5a14316c7ffefc588771f491c709152
     mtn: [source] 8b41b5399a564494993063287a737d26ede3dee4
     mtn: common ancestor 70decb4b31a8227a629c0e364495286c5c75f979 abe@juicebot.co.jp  2004-10-26T:02:50:01 found
     mtn: trying 3-way merge
     mtn: [merged] da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: your workspaces have not been updated

The output of this command shows Jim that two heads were found, combined via a 3-way merge with their ancestor, and saved to a new revision. This happened automatically, because the changes between the common ancestor and heads did not conflict. If there had been a conflict, monotone would have invoked an external merging tool to help resolve it.

After merging, the branch has a single head again, and Jim updates his workspace.

     $ mtn update
     mtn: selected update target da499b9d9465a0e003a4c6b2909102ef98bf4e6d
     mtn: updating src/apple.c to f088e24beb43ab1468d7243e36ce214a559bdc96
     mtn: updating src/banana.c to 5e6622cf5c8805bcbd50921ce7db86dad40f2ec6
     mtn: updated to base revision da499b9d9465a0e003a4c6b2909102ef98bf4e6d

The update command selected an update target — in this case the newly merged head — and performed an in-memory merge between Jim's workspace and the chosen target. The result was then written to Jim's workspace. If Jim's workspace had any uncommitted changes in it, they would have been merged with the update in exactly the same manner as the merge of multiple committed heads.

Monotone makes very little distinction between a “pre-commit” merge (an update) and a “post-commit” merge. Both sorts of merge use the exact same algorithm. The major difference concerns the recoverability of the pre-merge state: if you commit your work first, and merge after committing, then even if the merge somehow fails (due to difficulty in a manual merge step, for instance), your committed state is still safe. If you update, on the other hand, you are requesting that monotone directly modify your workspace, and while monotone will try hard not to break anything, this process is inherently more open to error. It is therefore recommended that you commit your work first, before merging.

If you have previously used another version control system, this may at first seem surprising; there are some systems where you are required to update, and risk the above problems, before you can commit. Monotone, however, was designed with this problem in mind, and thus always allows you to commit before merging. A good rule of thumb is to only use update in workspaces with no local modifications, or when you actually want to work against a different base revision (perhaps because finishing your change turns out to require some fixes made in another revision, or because you discover that you have accidentally started working against a revision that contains unrelated bugs, and need to back out to a working revision for testing).

2.12 Branching and Merging

So by now you're familiar with making changes, sharing them with other people, and integrating your changes with their changes. Sometimes, though, you may want to make some changes, and not integrate them with other people's — or at least not right away. One way to do this would be to simply never run mtn merge; but it would quickly become confusing to try and keep track of which changes were in which revisions. This is where branches are useful.

Continuing our example, suppose that Jim is so impressed by Beth's work on banana juice support that he assigns her to work on the JuiceBot 7's surprise new feature: muffins. In the mean time, Abe will continue working on the JuiceBot's basic juice-related functions.

The changes required to support muffins are somewhat complicated, and Beth is worried that her work might destabilize the program, and interfere with Abe's work. In fact, she isn't even sure her first attempt will turn out to be the right approach; she might work on it for a while and then decide it was a bad idea, and should be discarded. For all these reasons, she decides that she will work on a branch, and then once she is satisfied with the new code, she will merge back onto the mainline.

She decides that since main development is in branch jp.co.juicebot.jb7, she will use branch jp.co.juicebot.jb7.muffins. So, she makes the first few edits to the new muffins code, and commits it on a new branch by simply passing --branch to commit:

     $ mtn commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework'
     mtn: beginning comm