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
- Successive Revisions
- Patch Levels and Development Phases
- Getting a Revision
- Optimizing Archives for get
- Finding Out What Changed
- The whats-missing Command
- Update
- Replay
- The Next Version

### 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`