Next: Branching and Merging, Previous: Making Changes, Up: Tutorial
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 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).