The Hackerlab at regexps.com

Basic Revision Control

up: arch
next: Basic Branching and Merging
prev: Development Paths

This chapter introduces the fundamental operations for storing revisions in archives, retrieving them, doing clever things with patches, and managing project trees for archived projects.


The First Revision

up: Basic Revision Control
next: Successive Revisions

When beginning a new project, the first step is to check in the very first revision of the tree.

Prepare the Archive We've already seen how to prepare the archive by creating the category, branch, and version for the project (see Creating a Development Path). You have to perform those steps before checking in the first revision:

        % larch make-category CATEGORY
        % larch make-branch BRANCH
        % larch make-version VERSION

Prepare the Project Tree We've also seen how to turn an ordinary directory into a project tree using the init-tree command (see Initializing a Project Tree). And we've seen how to set the default version of a project tree using set-tree-version (see Labelling Project Trees). Both of these steps must be completed before checking in the first revision:

        % larch init-tree
        % larch set-tree-version VERSION-NAME

In addition, you must create for the project tree a "patch log" -- where log entries for revisions in the development path VERSION-NAME will be stored:

        % larch add-log VERSION-NAME

Patch logs are explained in detail in the next chapter. For now, just accept the necessity of that command on faith.

Prepare a Log Message In the root of the project tree, the command:

        % larch make-log 

creates a template for a log file (look for file whose name begins with ++log ). The details of what should go in a log message are project specific. In general, the format of a log message uses RFC822 style headers followed by a free-form body. When arch stores a log message, it will add some headers of its own.

Archive the Tree Again, in the root of the project tree, use the import command to archive the tree:

        % larch import

import will print the headers of the log message (including headers added by arch ), store the tree in the archive (as a compressed tar file), and update the patch log of the working directory to reflect the commit.

After commiting a revision to, say, hello--devo--1.0 , you can use the command revisions to see a list of archived revisions:

        % larch revisions hello--devo--1.0
        base-0

base-0 is the "patch level name" for the first revision. Patch levels are described in greater detail below.


Successive Revisions

up: Basic Revision Control
next: Patch Levels and Development Phases
prev: The First Revision

Suppose that you have continued to edit your working directory after checking in the initial revision. Successive revisions can be checked in with the command commit , issued at the root of the working directory. As with import , you must first use make-log to prepare a log message for each commit:

        % larch make-log

        [...edit log message...]

        % larch commit
        [output omitted]

After several commits, you'll have a number of patch levels:

        % larch revisions hello--devo--1.0
        base-0
        patch-1
        patch-2
        patch-3
        ...

As a point of interest, the base revision is stored as a compressed tar file of the entire tree. Each patch-N is stored as a compressed tar file of just a patch set that describes how to derive that revision from the previous revision.


Patch Levels and Development Phases

up: Basic Revision Control
next: Getting a Revision
prev: Successive Revisions

Within a version, there is a sequence of revisions, each of which is a different patch level . Every patch level has a patch level name , derived from the version name by adding a suffix:

        hello--devo--1.0--base-0
        hello--devo--1.0--patch-1
        hello--devo--1.0--patch-2
        hello--devo--1.0--patch-3
        ...

Just as with version names, there is also such a thing as a fully qualified patch level name :

        joe.hacker@gnu.org--test-archive/hello--devo--1.0--patch-3

Development within a version may be optionally divided into four phases: base revision , pre-patches , version revision , and post-patches . Conceptually, "pre-patches" are revisions made before the version is "done". "Post-patches" are revisions made after the version is done (e.g. "bug fix patches").

At the beginning of a development path is the base revision (called base-0 ). Between the pre-patch and post-patch phases is the version revision (called version-0 ). Post-patches have names like versionfix-1 , versionfix-2 , etc.

So the total sequence of revisions within a development path has the form:

        The Initial Revision:

                base-0

        Pre-Patches:

                patch-1
                patch-2
                patch-3
                ...
                patch-N

        The Version Revision:

                version-0

        Post-Patches:

                versionfix-1
                versionfix-2
                versionfix-3
                ...

Typically, the version-0 revision is what would be released under the version number and the post-patch revisions are fixes made after the release.

The ordinary commit command creates pre-patches (patch-N revisions).

To create the version revision, use commit --seal :

        % larch commit --seal

After the version revision exists, ordinary commit will no longer work. To create a post-patch revision, use commit --fix :

        % larch commit --fix

It is perhaps worth mentioning that patch level names can be sorted easily using:

       sort -t - -k 1,1 -k 2,2n

or in reverse:

       sort -t - -k 1,1r -k 2,2rn

If you are familiar with other revision control systems, a four-phased development process may at first seem somewhat arbitrary and needlessly complicated. Two points are worth mentioning:

First, phased development is entirely optional. Nothing requires you to ever --seal a version, and if you never seal a version, you never need to use --fix . Instead, every revision (after base-0 ) will be a patch-N revision.

Second, phased development is a handy way to prevent accidents when organizing more complex projects. Sealing a version is a way to set a flag that says, in effect, "ordinary development in this version has stopped -- you might not really want to make a new revision here." You can make a new revision if you insist (by specifying --fix ), but the requirement that you insist helps alert you to the fact that your new revision might need to be merged with later versions of the same project; or that that version you are revising has already been released. Phased development is a bookkeeping convenience: for the most part, arch treats all revisions equally, regardless of their phase.


Getting a Revision

up: Basic Revision Control
next: Optimizing Archives for get
prev: Patch Levels and Development Phases

To retrieve a revision from an archive, use larch get :

        % larch get REVISION DIR

as in:

        % larch get hello--devo--1.0--patch-4 hello

to retrieve the revision patch-4 and store it in the new directory hello .

An abbreviation can be used to obtain the most recent revision of a version:

        % larch get hello--devo--1.0 hello

or to obtain the most recent revsion of the highest-numbered version:

        % larch get hello--devo hello

A fully-qualified name can be used to obtain a revision from someplace other than the default archive:

        % larch get joe.hacker@gnu.org--test-archive/hello--devo


Optimizing Archives for get

up: Basic Revision Control
next: Finding Out What Changed
prev: Getting a Revision

The way that get ordinarilly works is that it searches backwards from the desired revision to find the nearest full-source base revision. It gets the compressed tar file for that base revision and creates a source tree. Then, for each intermediate patch level, it gets a compressed tar file of the patch set, uncompresses and un-tars the patch-set, and applies the patch-set to the source tree.

If there are many intermediate patch-sets, that process can be slow. In such cases, you can ask arch to cache a full-source copy of an arbitrary revision, with the command:

        % larch archive-cache-revision [ARCHIVE/]REVISION

That command first builds the requested revision, then it builds a compressed tar file of the revision, then it stores the tar file back in the archive. Subsequent attempts to get the same revision (or any later revision) will use the cached tree.

To remove a previously cached tree, use:

        % larch archive-uncache-revision [ARCHIVE/]REVISION

For each user, arch also maintains a "client side" cache of revisions that can speed up get (and other operations). The details of client side caching are documented in a later chapter (xref!!!).


Finding Out What Changed

up: Basic Revision Control
next: The whats-missing Command
prev: Optimizing Archives for get

Before performing a commit , you might want to check to see what has actually changed -- that is, find out exactly what patch set your commit will create.

You can do that with the command what-changed :

        % larch what-changed
        [...patch set report...]

what-changed computes a patch set between your modified project tree and the latest patch level for which your project tree is up-to-date. In other words, it tells you what changes have been made to your tree compared to the tree in the archive.

what-changed leaves behind a directory containing the patch set and patch report, which you can usefully browse by hand.

    % ls
    ,,what-changed.arch--devo--0.5--patch-14--lord@regexps.com--arch-1
    [...]

The default output of what-changed is formatted for use with the outline mode of GNU Emacs.

You can ask what-changed to also generate an HTML-formatted report with:

        % larch what-changed --url
        URL of patch report

The HTML report has links to each context diff, added, and removed file. The output of the command is a file: method URL. For example, a useful command for netscape users is:

        % netscape --remote "openURL(`larch what-changed --url`)"


The whats-missing Command

up: Basic Revision Control
next: Update
prev: Finding Out What Changed

Suppose that more than one programmer is checking revisions into a version, Alice and Bob for example.

Alice and Bob both start with working directories and both make some changes. Alice commits several changes. Now Bob's working directory has fallen behind archived development path.

The command whats-missing can be used to tell Bob which patches he is missing:

        % larch whats-missing --summary
        patch-N
                summary of patch N
        patch-N+1
                summary of patch N+1 
        ...

For each patch that Bob is missing, whats-missing --summary prints the name of the patch and the contents of the Summary: header from its log message.

The whats-missing command is explained in greater detail in a later chapter (see Patch Logs and ChangeLogs).


Update

up: Basic Revision Control
next: Replay
prev: The whats-missing Command

So Bob is behind by a few patches, but also has his own modifications.

Diagramatically, we have something like:

        Patch Levels    Bob's Working
        in the          Directory
        Archive:
        ------------------------------

        base-0

        patch-1
        patch-2
        patch-3
        patch-4 --------> bob-0 (Bob's initial working directory)
        patch-5             |
        patch-6             |
        patch-7             V
        patch-8           bob-1 (Bob's working dir with changes)

Bob is missing patches five through eight.

The command update can be used to fix the situation:

        % larch update OLD-DIR NEW-DIR

as in:

        % larch update bob-1 bob-2

update works in several steps. First, it gets a copy of the latest revision (patch-8 in this case). It also gets a copy of the revision from which OLD-DIR is dervied (patch-4 in this case). Then it uses mkpatch to compute the differences between OLD-DIR and its source revision, and applies those differences (using dopatch ) to the latest revision.

In the example, update will create the directory bob-2 , with the source:

        delta(patch-4, bob-1)[patch-8]

(For information about this notation, see The Theory of Patches and Revisions.)

Applying that patch might cause conflicts. In that case, update will print a message telling Bob to look for .rej files.

As a convenience, update also copies all "precious" non-source files from OLD-DIR to NEW-DIR (see arch Project Inventories).

Of course, if Bob only wanted to "partly update", he could do that with an extra parameter to update , as in the example:

        # update, but only up to patch level 6:
        # 

        % larch update bob-1 bob-2 hello--devo--1.1--patch-6

Finally, update can replace OLD-DIR with the updated directory if given the --in-place flag:

        % larch update --in-place OLD-DIR


Replay

up: Basic Revision Control
next: The Next Version
prev: Update

update isn't the only way to catch-up with a development path. Another option is replay :

        % larch replay old-dir new-dir

Using the same example:

        Patch Levels    Bob's Working
        in the          Directory
        Archive:
        ------------------------------

        base-0

        patch-1
        patch-2
        patch-3
        patch-4 --------> bob-0 (Bob's initial working directory)
        patch-5             |
        patch-6             |
        patch-7             V
        patch-8           bob-1 (Bob's working dir with changes)

and the command:

        % larch replay bob-1 bob-2

replay will first copy bob-1 to create bob-2 . Then it will apply each missing patch in succession until bob-2 is up-to-date, or until a merge conflict occurs.

Thus, if no conflict occurs, replay computes:

        patch-8 [ patch-7 [ patch-6 [ patch-5 [ bob-1 ]]]]

If a conflict occured in, say, patch-6 , then replay would compute:

        patch-6 [ patch-5 [ bob-1 ]]

and after fixing the conflict, Bob could use a second replay command to apply patches seven and eight.

As with update , replay also copies all "precious" non-source files from OLD-DIR to NEW-DIR (see arch Project Inventories).

Of course, if Bob only wanted to "partly replay", he could do that with an extra parameter to replay , as in the example:

        # replay, but only up to patch level 6:
        # 

        % larch replay bob-1 bob-2 hello--devo--1.1--patch-6

You can use replay to modify an existing directory rather than creating a new directory:

        % larch replay --in-place DIR [REVISION]

but be careful: if DIR contains precious local changes, and conflicts occur, or if you simply decide the replay wasn't a good idea, you'll have to do some work to revert the replay .


The Next Version

up: Basic Revision Control
prev: Replay

In simple situations, a version like hello--devo--1.0 will be followed by the next version : hello--devo--1.1 or hello--devo--2.0 , for example.

Such a version is called a continuation of the previous version and it is created with this sequence of commands (in the root of a working directory):

        % larch make-version NEXT-VERSION
        % larch add-log NEXT-VERSION
        % larch set-tree-version NEXT-VERSION
        % larch make-log
        [...edit log message...]
        % larch commit --continuation PREVIOUS-VERSION

as in:

        % larch make-version hello--devo--1.1
        % larch add-log hello--devo--1.1
        % larch set-tree-version hello--devo--1.1
        % larch make-log
        [...edit log message...]
        % larch commit --continuation hello--devo--1.0

Those commands create the base-0 revision of the new version but instead of storing complete source for the base revision, they store a pointer to the older revision which the base revision is equal to.

On the other hand, if you wanted the next version to start completely from scratch (perhaps because a program is being replaced by an entirely new implementation), the import command can create the next version as documented earlier in this chapter.

Finally, there is another command (besides commit --continuation ) for creating the next version, larch tag , described in the next chapter.

arch: The arch Revision Control System
The Hackerlab at regexps.com