Next: Branching and Merging, Previous: Making Changes, Up: Tutorial [Contents][Index]
Careful readers will note that, in the previous section, the JuiceBot company’s work was perfectly serialized:
The result of this ordering is that Jim’s work entirely 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:
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 85573... 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 [85573a54105cd3220db10aa6a0713643cdf5ce6f] # # patch "src/banana.c" # from [d7e28a01cf6fc0f9ac04c6901dcafd77c2d32fb8] # to [dd979c3c880e6a7221fcecd7148bd4afcfb3e964] # ============================================================ --- src/banana.c d7e28a01cf6fc0f9ac04c6901dcafd77c2d32fb8 +++ src/banana.c dd979c3c880e6a7221fcecd7148bd4afcfb3e964 @ -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() { + 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 90abe0f1bc354a73d42d3bff1b02946559682bd9
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 42eae..., 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: 90abe0f1bc354a73d42d3bff1b02946559682bd9 abe@juicebot.co.jp 2004-10-26T02:53:16 951da88860a4cf7419d66ed9094d8bf24df5fb8b 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: 2 heads on branch 'jp.co.juicebot.jb7' mtn: merge 1 / 1: mtn: calculating best pair of heads to merge next mtn: [left] 90abe0f1bc354a73d42d3bff1b02946559682bd9 mtn: [right] 951da88860a4cf7419d66ed9094d8bf24df5fb8b mtn: [merged] 3aca69c7749bde9bd07fe4c92bb868bd69b2e421 mtn: note: 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, or Jim could have used the conflicts
set of
commands to resolve it (see Conflicts).
After merging, the branch has a single head again, and Jim updates his workspace.
$ mtn update mtn: updating along branch 'jp.co.juicebot.jb7' mtn: selected update target 3aca69c7749bde9bd07fe4c92bb868bd69b2e421 mtn: [left] d60c18ec5e0cf1163b276f0bfdd908c1dfd53b4a mtn: [right] 3aca69c7749bde9bd07fe4c92bb868bd69b2e421 mtn: updating src/apple.c mtn: updating src/banana.c mtn: updated to base revision 3aca69c7749bde9bd07fe4c92bb868bd69b2e421
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).
Next: Branching and Merging, Previous: Making Changes, Up: Tutorial [Contents][Index]