regexps.com
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.
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.
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.
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.
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
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!!!).
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`)"
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).
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
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
.
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.
regexps.com