CVSLINES(1)

NAME
cvslines - a CVS wizard to help manage parallel development

SYNOPSIS
cvslines [status] [-l|-R] file|dir ...

cvslines checkout [-d dir] line module | -t module

cvslines commit [-n] [-l|-R] [-x|-X] [-b] [-o] [-i] [-q]
                [-a] [-f msgfile | -m msg ]
                [-all[-line...] | -only[+line...]]
                file|dir ...

DESCRIPTION
cvslines is, in effect, a "wizard" helper for use with the standard cvs(1) command. cvslines helps users of cvs modules representing multiple parallel "lines of development" to manage the task of merging changes from one such line into others at commit time, as appropriate. These lines of development (hereafter referred to simply as "lines") typically represent different releases of a software system. (These are often thought of as "branches". However, cvslines reserves the the term "branch" to refer to the more precise definitions as they are used in rcs(1) and cvs).

OPTIONS
cvslines supports the following options:

-l  Like the cvs -l option: only act on files in the current directory, (rather than recursively into any subdirectories).

-R  Like the cvs -R option: perform the action recursively, down through the tree rooted at the current directory. (This is the default, but -R can be useful to override a "$Norecurse=1;" placed in the ~/.cvslinesrc file).

-n  ["cvslines commit" only] Like the cvs -n option: cvslines will execute the commit planning phase as usual, any will show the steps that would be carried out to implement the commit plan, but will not actually execute them.

-x  This option disables the "[press Return to]:" prompts that cvslines normally presents to the user at each step in executing the commit plan.

-X  This option enables the "[press Return to]:" prompts. The prompts are enabled by default, but -X can be useful to to override a "$Noaskexec=1;" placed in the ~/.cvslinesrc file).

-b  This option tells cvslines to run in "batch" mode. This has the effects of:

    (1) Allowing cvslines to function when the standard IO streams are not a tty;

    (2) Making cvslines require that a log message be supplied with either a -m or -f option, and the lines to be commit to be specified with either a -all or -only option;

    (3) cvslines will always skip a commit for which merge conflicts are detected, and produce a summary show the list of files so skipped at the end of the cvslines execution.

    (4) forces on the -x option; and

    (5) Causes the "Proceed?" prompt at the end of the planning phase for each file to be skipped.

-o  This option requests that cvslines status only display information about lines that are members of the same "lines group" as the one represented by the current working tree. (See the "LINES GROUPS" section, below, for more information about "lines groups".

-i  This option turns off the "sticky answers" feature. (By default, cvslines will only ask about whether a commit should be applied to a given line of development once, the first time the line is encountered; if a cvslines commit operation acts upon more than a single file, then the same answer will be assumed for each subsequent file. The -i option causes cvslines to ask whether the commit should be applied for each line on a per-file basis.

-q  Causes cvslines to be less verbose. The following messages will be suppressed:

    (1) The per-file preamble:
          cvslines: making commit plan for:
                file: ...
            cvs info: ...
             line       spec            spec rev               cur rev
             ---------- --------------  ---------------------- ----------
             .
             .
             .

    (2) The "Action Plan" summary:
          Action plan:

             .
             .
             .

-a  Turn off lines groups awareness altogether (i.e., consider all lines to be part of a single lines group, disregarding any lines groups defined in cvslines.config.

-f msgfile
    This option tells cvslines to use the text found in msgfile as the log message to be supplied for each cvs commit operation performed.

-m msg
    This option tells cvslines to use the text msg as the log message to be supplied for each cvs commit operation performed.

-all[-line...]
    The -all option tells cvslines to assume an answer of "yes" to all "should this change also be applied to line..." and/or "should should file also be included in line..." questions that arise during a "cvslines commit" operation. The optional set of trailing -line arguments allow you to explicitly specify exceptions, i.e., the names of lines of development for which the answer "no" should be used. For example, "-all-R1.0" would specify that the answer "yes" should be assumed for all lines except one named "R1.0".

-only[+line...]
    This option is the inverse of the "-all" option: it tells cvslines to assume an answer of "no" to all "should this change also be applied to line..." and/or "should should file also be included in line..." questions, except for any lines named by +line arguments.

-d dir
    This option, defined only for the cvslines checkout subcommand, is passed along as a -d option to the cvs checkout command, causing the named module to be checked out into a directory called dir.

-t module
    This option, defined only for the cvslines checkout subcommand, requests that cvslines merely display the set of known line entries from the cvslines.config file for the named module.

THE cvslines.config FILE
To inform cvslines about the set of lines that are under its management, each cvs module under cvslines control has one (or more) cvslines.config file(s), in the module's top-level repository directory(s).

The cvslines.config file is typically set up by your cvs administrator; most other users can safely skip this section.

If a cvs module is defined to include multiple files and/or directories, then the cvslines.config file should be replicated in the root of each directory specified. E.g., for a $CVSROOT/CVSROOT/modules entry of
  tmod

You would need a single cvslines.config file
  $CVSROOT/tmod/cvslines.config

For a module defined as
  tmod	dir1 file1.c file1.h
  tmod	dir2 file2.c file2.h

you would need to replicate the cvslines.config file as
  $CVSROOT/dir1/cvslines.config
  $CVSROOT/dir2/cvslines.config

(For convenience of administration, you could make the multiple cvslines.config files be links to the same file).

For example, for a module "tmod", this would be "$CVSROOT/tmod/cvslines.config".

The cvslines.config file has the following format
  #
  #  This is an example cvslines.config file. It also serves as the
  #  official documentation for the format of cvslines.config files.
  #
  #  Each cvs module repository for which cvslines is to be used gets
  #  a cvslines.config file in the root of the repository tree (where
  #  the RCS ,v files for the cvs module are kept).
  #
  #  The file consists of two segments...
  #
  #  I. The user list.
  #
  #  This specifies the set of users that should be doing commits for
  #  this module using the "cvslines commit" facility. (When such
  #  users try to directly execute "cvs commit", they'll be remined to
  #  use "cvslines commit", instead).
  #
  #  Each line is "~<loginname>" or "!~<loginname>".  The special
  #  entry "~ALL" means "all users".  "!~<loginname> means that the
  #  specified user should *never* be reminded to use "cvslines
  #  commit".
  #
  ~ALL

  #  II. The lines declarations.
  #
  #  Other entries specify lines of development known to this
  #  module. Each line is four whitespace-separated fields
  #
  #    name     the name of the line [max 10 chars]
  #
  #    spec     the cvs specification, identifying revisions on that
  #             line for a given file. It must be either a cvs branch
  #             tag or "head". [max 14 chars]
  #
  #    state    the development state for the line.
  #             [Currently, nothing uses this].
  #
  #    opts     a set of comma separated options that apply to this
  #             line. Currently, only "+<name>" is defined.  This
  #             causes cvslines to make the default for the "include
  #             these changes in <line>" "yes" for the <name> line.
  #             Use "-" for an empty options field if you also need
  #             to specify an lgroup.
  #
  #    lgroup   the "lines group" tag. The default lgroup name is
  #             "" (empty), so you need not specify anything here
  #             if you are not going to define additional lgroups.
  #
  #name    spec           state   opts	  lgroup
  R1.2     head           dev
  R1.1     R1_1_BRANCH    dev     +R1.2
  R1.0     R1_0_BRANCH    qa

THE $CVSROOT/commitinfo ENTRIES
In order to tell cvs that a given module should have commits controlled via the "cvslines commit" command (rather than directly via the normal "cvs commit" command), cvslines uses a pair of entries in the $CVSROOT/commitinfo file, for example
  ^tmod$ /usr/local/bin/cvslines_check
  ^tmod/ /usr/local/bin/cvslines_check

This causes cvs to execute the cvslines_check command for each directory containing files to be committed when changes to files in the "tmod" module are committed.

THE cvslines checkout COMMAND
The "cvslines checkout" command can be used to make cvslines translate the specified line name into the correct cvs branch tag for checking out a tree for a given line of development. The translated name is supplied in a "cvs checkout" command, which is displayed and then automatically executed for you. E.g.:
  % cvslines checkout R1.1 mod
  cvs checkout -r R1_1_BRANCH mod
  cvs checkout: Updating mod
  U mod/file1
  U mod/file2
  cvs checkout: Updating mod/dir1
  U mod/dir1/file3
  cvs checkout: Updating mod/dir1/dir2
  U mod/dir1/dir2/file4

You can use "cvslines checkout -t module" to get a list of the line names known for a specified module:
  % cvslines checkout -t mod
  R1.2     head           dev
  R1.1     R1_1_BRANCH    dev
  R1.0     R1_0_BRANCH    qa

THE cvslines status COMMAND
The "cvslines status" command (which is what you get by default if you just type "cvslines" without naming a subcommand) reports on the status of each line of development for each cvs-controlled file, as specified by the named files and/or directories. The file and directory arguments work just like they do in cvs: a directory name will cause all files in the directory to be reported (with directory recursion), and a file name will only do the named file. The "-l" option can be used to inhibit directory recursion, or the "-R" option to explicitly enable it.

For each file it reports on, "cvslines status" will show, for each line under cvslines control (as specified in the cvslines.config file for the module), the line name, the cvs branch specification, the actual numeric value represented by this specification, and the current RCS revision for the line.

For example:
  % cvslines -l
  ./file1
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
     R1.2       head            1.2                    1.2
  >> R1.1       R1_1_BRANCH     1.2.0.4                1.2
     R1.0       R1_0_BRANCH     1.2.0.2                1.2
  ./file2
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
     R1.2       head            1.2                    1.2
  >> R1.1       R1_1_BRANCH     1.2.0.2                1.2
     R1.0       R1_0_BRANCH     1.1.1.1.0.2            1.1.1.1

(The ">>" tags show that these lines are the ones represented by the working tree from within which cvslines was invoked.)

For lines that do not currently include the file, the spec rev will be shown as "head" or "none", for lines specified as "head" or with a branch tag, respectively. The value for "cur rev" for either will be shown as "none":
  % cvslines Newfile
  ./Newfile
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
     R1.2       head            head                   none
     R1.1       R1_1_BRANCH     none                   none
  >> R1.0       R1_0_BRANCH     1.1.0.2                1.1.2.1

If multiple "lines groups" are defined (See the "LINES GROUPS" section, below), then (by default) "cvslines status" will indicate lines that are not members of the same lines group as the current working tree's, with an "xx" tag before the line names:
  % ../../../cvslines status file2
  cvslines: *** running from "/n/makita/users/rmg/cvs/tools/cvslines" ***
  ./file2
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
  >> R1.2       head            1.2                    1.2
  xx projX      R_projX         1.2.0.4                1.2
  xx projY      R_projY         1.2.0.6                1.2
     R1.1       R1_1_BRANCH     1.2.0.2                1.2.2.1
     R1.0       R1_0_BRANCH     1.1.1.1.0.2            1.1.1.1.2.1

THE cvslines commit COMMAND - EXAMPLE
This section explains in broad terms how "cvslines commit" works. For a more detailed discussion, including how newly added files are handled and other special cases, please see the next section.

To employ cvslines for a module, commits of changes to files in the module should be done via "cvslines commit", instead of the usual "cvs commit" command.

In order to help remind users of this convention, attempts to commit files with "cvs commit" for a module under cvslines control will result in a message like:
  % cvs commit
  cvs commit: Examining .
  cvslines:

    Commits for this module should be done via the "cvslines commit"
    command.

    To force a "cvs commit" command from this module without cvslines
    commit checks, run cvs with $CVSLINES_NOCHECK set in the environment.

  cvs commit: Pre-commit check failed
  cvs [commit aborted]: correct above errors first!

To actually do the commit, you use "cvslines commit". You can name individual files and/or directories (which are treated recursively by default), just as you would with the ordinary cvs commit command. The "-l" option inhibits directory recursion.

You can force cvslines to allow you to do a direct cvs commit by running "cvs commit" with the variable $CVSLINES_NOCHECK set in the environment.

"cvslines commit" operates in two phases.

In the first ("planning") phase, it asks you, for each file, whether the changes being committed should also be applied to the other lines of development that contain the file. Based on your answers, cvslines decides on the proper cvs actions to take.

In the second ("execution") phase, cvslines helps you to execute the plans created in the first phase, supplying you with the command lines required and allowing you to execute them.

Here's an example, in which a single file is being committed. First, cvslines presents a summary of the current status of the file, with a line for each line of development in the module under cvslines control. The line prefixed with ">>" is the one represented by the file in the working directory. Other lines that contain the file are prefixed with either "u?" or "m?", indicating that the change would be applied with an update or a merge, should it also need to be applied to that line of development. (I.e., lines prefixed with "u?" have not yet diverged with respect to the one represented by the file in the working directory). The tag "a?" is used to indicate that a file being added by a commit might be applicable to another line.
  % cvslines commit file2
  cvslines: making commit plan for:
        file: /n/makita/users/rmg/cvs_xmpl/mod_1.1/file2
    cvs info: 1.2/Sun Aug  4 05:02:54 1996//R1_1_BRANCH
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
  u? R1.2       head            1.2                    1.2
  >> R1.1       R1_1_BRANCH     1.2.0.2                1.2
  m? R1.0       R1_0_BRANCH     1.1.1.1.0.2            1.1.1.1

Next, cvslines asks whether, for each of the other lines of development, you wish to also apply the changes to that line, and then proposes an "Action plan" based on the responses:
  Should these changes also be applied to...
     R1.0       (y n) [n]? y
     R1.2       (y n) [n]? y
  NOTE:

    This check-in would normally (i.e., without cvslines) cause a
    new RCS branch to be created.

    But, since you also want to apply the revision to all the other
    non-branched lines that share revision "1.2", it will be
    better to apply the change as a new revision to the "1"
    branch, and then update the branch tags for other lines.  This will
    help minimize actual divergence for this file in lines that don't
    need to diverge yet. This is reflected in the commit plan shown
    below.

  Action plan:

     R1.0      : update with a revision merge and commit [1.1.1.1.2.1]
     R1.1      : update with a branch tag update [1.3]
     R1.2      : update with a commit [1.3]
  Proceed (y n) [n]?

The action plans show, for each line that wants to get the changes, how they will be applied, and the RCS revision number that would result.

In the example above, the user wants the changes to go into all of the lines containing the file. cvslines has decided that this will require two commits, one to create revision 1.3 (which will update the R1.1 and R1.2 lines), and one (based on a merge) to create revision 1.1.1.2.1 (which will update the R1.0 line).

If the user agrees to the action plan, cvslines will move into the execution phase. (In this example, there was only one file being committed, so the execution phase for "file2" follows the planning phase directly. Had there been other files involved in the commit, their planning phases would be performed prior to the execution phases for any of them).

By default, cvslines displays each action it will take, and waits for the user to type a Return for confirmation before proceeding.

You can disable these confirmation prompts either by running "cvslines commit" with the -x option, or by placing the line "$Noaskexec=1; 1;" in your ~/.cvslinesrc file.

However, it's recommended that you go ahead and allow the confirmations, as it will help you to understand more about how cvslines uses cvs.

Continuing our example, cvslines begins to execute the plan for "file2":
  Proceed (y n) [n]? y
  cvslines: wrote /n/makita/users/rmg/cvs_xmpl/mod_1.1/cvslines.plan
  cvslines: executing plans for directory "/n/makita/users/rmg/cvs_xmpl/mod_1.1"...
  cvslines: executing plan for file /n/makita/users/rmg/cvs_xmpl/mod_1.1/file2/file2...
  cvslines: committing new revision 1.3...
  cvslines: [press Return to]> mv -f file2 file2.cvslines.23654.save

(The user presses Return in order to tell cvslines to proceed)
  cvslines: [press Return to]> cvs update -A file2
  cvs update: warning: file2 was lost
  U file2
  cvslines: [press Return to]> mv -f file2.cvslines.23654.save file2
  cvslines: [press Return to]> cvs commit file2

(The user enters the CVS log message in the usual manner)
  Checking in file2;
  /u/rmg/cvs_xmpl/cvs/mod/file2,v  <--  file2
  new revision: 1.3; previous revision: 1.2
  done
  cvslines: merging to new revision 1.1.1.1.2.1...
  cvslines: [press Return to]> cvs update -r R1_0_BRANCH file2
  U file2
  cvslines: [press Return to]> cvs update -j 1.2 -j 1.3 file2
  RCS file: /u/rmg/cvs_xmpl/cvs/mod/file2,v
  retrieving revision 1.2
  retrieving revision 1.3
  Merging differences between 1.2 and 1.3 into file2
  rcsmerge: warning: conflicts during merge
  cvslines: merge conflicts detected.
  cvslines: skip or edit (s e) [e]?
  cvslines: [press Return to]> emacs file2

(The user is placed into $EDITOR to resolve merge conflicts)
  cvslines: commit, skip or edit (c s e) [c]?
  cvslines: [press Return to]> cvs commit -F cvslines.logmsg file2
  Checking in file2;
  /u/rmg/cvs_xmpl/cvs/mod/file2,v  <--  file2
  new revision: 1.1.1.1.2.1; previous revision: 1.1.1.1
  done
  cvslines: set branch tag "R1_1_BRANCH" to revision 1.3...
  cvslines: [press Return to]> cvs tag -F -r 1.3 -b R1_1_BRANCH file2
  T file2
  cvslines: restoring working tag to "R1_1_BRANCH"...
  cvslines: [press Return to]> rm -f file2; cvs update -r R1_1_BRANCH file2
  cvs update: warning: file2 was lost
  U file2
  %

In the example above, the steps taken to include the changes in the R1.0 line demonstrate how cvslines handles merging. In this case, the CVS merge facility detected merge conflicts, which must be resolved by the user before committing the file. In such cases, cvslines allows the user to either edit the file and proceed, or to "skip" the commit, in which case a copy of the partially merged file will be saved for later attention, and further actions relying on that revision will be skipped. For example, has the user chosen "skip" in the example above:
  cvslines: merge conflicts detected.
  cvslines: skip or edit (s e) [e]? s
  cvslines: [press Return to]> rm -f file2-conflicts_R1_0_BRANCH; cp file2 file2-conflicts_R1_0_BRANCH

THE cvslines commit COMMAND - BRANCH TAG UPDATES
This section documents, in greater detail, how cvslines handles particular cases that can arise in carrying out the action plans it has generated.

In general, cvslines will try to avoid actions that would result in the creation of new RCS branches where the tip of the branch doesn't really differ from the tip of its parent branch. This can occur with cvs-controlled modules, for example, when a release branch has been declared (by branch-tagging the files), and a change is applied in a branched working directory.

Normally, this will result in the creation of a new RCS branch, since (as far as cvs knows), the change represents a change that should be applied to the branch, only. This is fine, if indeed the same change needn't be carried forward into the new version of the software (on the parent RCS branch). But, if the change is also desired on the parent branch, it will need to be checked in separately there, too. This results in two distinct RCS revisions that are actually identical. (Except perhaps, in their RCS ident information expansions).

Furthermore, this consigns all such future changes (that need to go onto both branches) to needing a pair of check-ins, one for each branch.

cvslines avoids this situation, where possible, by using the strategy of checking in the changes to the tip of the existing RCS branch, and simply updating the branch tag in the branched line of development. This could also be done manually, of course, if the user remembers to think about it beforehand, and knows the right set of cvs commands to use. cvslines helps out by reminding the user about the situation, and, based on the user's desire about applying the change to both branches, by supplying the necessary cvs commands in order to make the commit go onto the parent branch and to update the branch tags.

cvslines can handle the situation described above equally well from a working directory corresponding to either line of development.

THE cvslines commit COMMAND - MERGES
In other cases, when the revisions on the tips of different lines have previously diverged, cvslines can still help out with the mechanics of merging a set of changes and checking them in appropriately. It does this using a "cvs update -jrev1 -jrev2" command, which in turn uses the rcsmerge(1) command.

When merge conflicts are detected, the user is given the opportunity to either edit the conflicts out of the file, and continue with the check-in, or to save away a copy of the file containing the conflicts, to be dealt with later.

cvslines will properly handle cases where changes are merged onto the tip of an existing RCS branch, and/or lines that exist as branch tags attached to such a branch.

THE cvslines commit COMMAND - ADDED FILES
cvslines can also help when new files are being added to a module. As for commits of changes modifying existing files, cvslines asks the user whether newly added files being committed into the module should also be included in the other lines of development, and arranges to do the right things, based on the user's answers. For example...
  % cvs add Newfile
  cvs add: scheduling file `Newfile' for addition on branch `R1_1_BRANCH'
  cvs add: use 'cvs commit' to add this file permanently
  % cvslines commit Newfile
  cvslines: making commit plan for:
        file: /n/makita/users/rmg/cvs_xmpl/mod_1.1/Newfile
    cvs info: 0/Initial Newfile//R1_1_BRANCH
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
  a? R1.2       head            (none)                 (none)
  >> R1.1       R1_1_BRANCH     (none)                 (none)
  a? R1.0       R1_0_BRANCH     (none)                 (none)
  Should this file be included in...
     R1.0       (y n) [n]?
     R1.2       (y n) [n]? y
  Action plan:
     R1.0      : will not be updated
     R1.1      : update with a branch tag create [1.1]
     R1.2      : update with a commit [1.1]
  Proceed (y n) [n]? y
  cvslines: wrote /n/makita/users/rmg/cvs_xmpl/mod_1.1/cvslines.plan
  cvslines: executing plans for directory "/n/makita/users/rmg/cvs_xmpl/mod_1.1"...
  cvslines: executing plan for file /n/makita/users/rmg/cvs_xmpl/mod_1.1/Newfile/Newfile...
  cvslines: committing new revision 1.1...
  cvslines: fool cvs into doing the right thing (don't panic!)...
  cvslines: [press Return to]> mv -f CVS/Tag CVS/Tag-
  cvslines: [press Return to]> mv -f Newfile Newfile.cvslines.6313.save
  cvslines: [press Return to]> cvs remove Newfile
  cvs remove: removed `Newfile'
  cvslines: [press Return to]> mv -f Newfile.cvslines.6313.save Newfile
  cvslines: [press Return to]> cvs add Newfile
  cvs add: scheduling file `Newfile' for addition
  cvs add: use 'cvs commit' to add this file permanently
  cvslines: [press Return to]> cvs commit Newfile

(The user enters the CVS log message in the usual manner)
  RCS file: /u/rmg/cvs_xmpl/cvs/mod/Newfile,v
  done
  Checking in Newfile;
  /u/rmg/cvs_xmpl/cvs/mod/Newfile,v  <--  Newfile
  initial revision: 1.1
  done
  cvslines: [press Return to]> mv -f CVS/Tag- CVS/Tag
  cvslines: set branch tag "R1_1_BRANCH" to revision 1.1...
  cvslines: [press Return to]> cvs tag -F -r 1.1 -b R1_1_BRANCH Newfile
  T Newfile
  cvslines: restoring working tag to "R1_1_BRANCH"...
  cvslines: [press Return to]> rm -f Newfile; cvs update -r R1_1_BRANCH Newfile
  cvs update: warning: Newfile was lost
  U Newfile
  % cvslines Newfile
  ./Newfile
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
     R1.2       head            1.1                    1.1
  >> R1.1       R1_1_BRANCH     1.1.0.2                1.1
     R1.0       R1_0_BRANCH     none                   none
  %

Note that cvslines had to go through some slight contortions in order to convince cvs to add the file as a new revision on the main trunk, rather than initially onto a branch, as a "normal" cvs commit would do in this situation. The result is, nonetheless, what was desired; the file now exists in both lines of development, and remains undiverged.

THE cvslines commit COMMAND - NEWLY IMPORTED FILES
There is one case where cvslines will actually move a branch tag from a second-level RCS branch back onto the trunk. This happens for files that have not been modified with any check-ins since being added to a module for the first time with "cvs import".

Here's what such a file looks like:
  % cvslines file3
  ./file3
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
  >> R1.2       head            1.1                    1.1
     R1.1       R1_1_BRANCH     1.1.1.1.0.4            1.1.1.1
     R1.0       R1_0_BRANCH     1.1.1.1.0.2            1.1.1.1

This is the state that a file is in following a cvs import, and two subsequent branch taggings (but before any changes have ever been checked in.)

Note that, as far as CVS is concerned, the branched revisions are branched off of the "vendor's release" (at revision 1.1.1.1), not off of the head revision (1.1). But, in reality, of course, no divergence has taken place.

In order to prevent actual branching, a cvslines commit to a file in this state will treat the file as if branches were off of the trunk. It alerts you to this by placing a "*" character next to the "spec rev" reported for such a line:
  % cvslines commit file3

  cvslines: making commit plan for:
        file: /n/makita/users/rmg/cvs_xmpl/mod_1.2/dir1/file3
    cvs info: 1.1.1.1/Sun Aug  4 04:52:58 1996//head

     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
  >> R1.2       head            1.1                    1.1
  u? R1.1       R1_1_BRANCH    *1.1.0.2                1.1
  u? R1.0       R1_0_BRANCH    *1.1.0.2                1.1

  Should these changes also be applied to...
     R1.0       (y n) [n]? y
     R1.1       (y n) [n]? y

  Action plan:

     R1.0      : update with a branch tag update [1.2]
     R1.1      : update with a branch tag update [1.2]
     R1.2      : update with this commit [1.2]

  Proceed (y n) [n]? y

  cvslines: wrote /n/makita/users/rmg/cvs_xmpl/mod_1.2/dir1/cvslines.plan
  cvslines: executing plans for directory "/n/makita/users/rmg/cvs_xmpl/mod_1.2/dir1"...
  cvslines: executing plan for file /n/makita/users/rmg/cvs_xmpl/mod_1.2/dir1/file3/file3...
  cvslines: committing new revision 1.2...
  cvslines: [press Return to]> cvs commit file3

(The user enters the CVS log message in the usual manner)
  Checking in file3;
  /u/rmg/cvs_xmpl/cvs/mod/dir1/file3,v  <--  file3
  new revision: 1.2; previous revision: 1.1
  done
  cvslines: set branch tag "R1_0_BRANCH" to revision 1.2...
  cvslines: [press Return to]> cvs tag -F -r 1.2 -b R1_0_BRANCH file3
  T file3
  cvslines: set branch tag "R1_1_BRANCH" to revision 1.2...
  cvslines: [press Return to]> cvs tag -F -r 1.2 -b R1_1_BRANCH file3
  T file3
  %

As shown above, when you choose to update such a branch, the branch tag gets moved onto the new revision checked in on the trunk, properly re-uniting the (as yet undiverged) branches with the trunk.

MULTIPLE head LINES
cvslines can also help to detect the time at which a new line should be declared for a module (i.e., by branching tagging the entire module).

To use it this way, you can have multiple line names sharing the specifier "head".

When a user committing a change to any line specified by "head" declines to include the change in all such lines, then cvslines balks, and recommends that the module actually be branched.

When using such multiple "head" lines, cvslines needs a way to know, when in a working tree representing one of the "head" lines, which one it's in. (There's no way to infer this from normal cvs administrative files as there is for trees representing branch lines). "cvslines status" will simply show all lines set to "head" as being the current line:
  % cvslines file1
  ./file1
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
  >> R1.2.1     head            1.2                    1.2
  >> R1.2       head            1.2                    1.2
     R1.1       R1_1_BRANCH     1.2.0.4                1.2
     R1.0       R1_0_BRANCH     1.2.0.2                1.2

When it can't resolve the line name in this situation, "cvslines commit" will report:
  % cvslines commit

  cvslines: making commit plan for:
        file: /n/makita/users/rmg/cvs_xmpl/mod_1.2/file1
    cvs info: 1.2/Sun Aug  4 04:58:18 1996//head

  cvslines: problem:

    Can't resolve the line name for the file

      /n/makita/users/rmg/cvs_xmpl/mod_1.2/file1

    Please correct this by setting the $CVSLINE environment variable,
    or creating a .../CVS/cvsline file at the top of your working tree,
    and try again.

In order to handle this and allow the commit to proceed, cvslines will look for either

1)  A file called "CVS/cvsline" relative to any directory from the working directory, up to "/", containing the line name declaring the identity of the line represented by the tree. (Usually such a file will be placed in the root directory of the working tree). -or-

2)  A environment variable "$CVSLINE", set to the line name.

In the event that a user attempts a commit, specifying that the changes are not wanted on all of the lines that share the head of the trunk, then cvslines will recommend:
  % CVSLINE="R1.2" cvslines commit
  cvslines: making commit plan for:
        file: /n/makita/users/rmg/cvs_xmpl/mod_1.2/file1
    cvs info: 1.2/Sun Aug  4 04:58:18 1996//head
     line       spec            spec rev               cur rev
     ---------- --------------  ---------------------- ----------------------
  u? R1.2.1     head            1.2                    1.2
  >> R1.2       head            1.2                    1.2
  u? R1.1       R1_1_BRANCH     1.2.0.4                1.2
  u? R1.0       R1_0_BRANCH     1.2.0.2                1.2
  Should these changes also be applied to...
     R1.0       (y n) [n]? y
     R1.1       (y n) [n]? y
     R1.2.1     (y n) [n]?
  cvslines: problem:
  You have indicated that you want some, but not all, of the
  non-branched lines on branch "1" to get this revision.
  In this case, the line(s) that should not get the revision should
  probably be moved onto a branch before applying this revision.
  This is a significant operation, which must be applied to all of the
  files in the module. Please consult with Release Engineering before
  proceeding with these changes.

THE $HOME/.cvslinesrc file
cvslines users can alter the default behavior of cvslines to some extent by creating entries in a file name ".cvslinesrc" in their home directories.

Currently, the the following defaults can be changed:

   $Norecurse=1;  # Don't recurse (like cvs -l) by default
   $Noaskexec=1;  # Don't prompt "[press Return to]" (like -x)
   $Noexec=1;     # Don't actually attempt commits (like -n)
   $Noconfirm=1;  # Don't prompt for plan confirmation ("Proceed?")
   $Verbose=0;    # Don't be as verbose (like -q)
   $Batch=1;	  # Use "batch" mode (like -b)
   $Showall=0;    # Don't show lines from other lgroups (like -o)
   $Stickyans=0;  # Prompt for each line, per-file (like -i)

The last line of the .cvslinesrc file must be "1;".

If you've flipped one of the above defaults in your $HOME/.cvslinesrc, then the alternate behavior can usually be requested by using one of the other associated option letters.

For example, if you have the following $HOME/.cvslinesrc file:

   % cat ~/.cvslinesrc
   $Noaskexec=1;
   1;
   %

then, for you, the default will be not to use the "[press Return to]" prompts. If you do want the prompts for a particular invocation, you can say "cvslines commit -X ...".

LINES GROUPS
Sometimes, an organization may wish to reserve certain lines of development for projects as being logically separate from others. That is, some group may need to work on one or more lines of development where the norm is to rarely merge updates onto other lines. cvslines helps to support this model by using a feature called "lines groups".

Lines groups are defined by designating line group names in the cvslines.config file. Every line is a member of exactly one lines group. By default (i.e., if the lgroup field for a line in the cvslines.config file is empty), then line is a member of the default lines group ("").

"cvslines commit" will only ask whether a change should be committed to other lines in the same lines group as the one for the working tree in which cvslines commit is invoked.

Normally, cvsline status will show the status for all lines. (It does, however, indicate lines outside of the current working tree's lines group with an "xx" tag). The -o option restricts it to showing the status only for lines in the same group as that of the working tree from which it is invoked.

Similarly, when summarizing a file's status, "cvslines commit" normally indicates lines that are outside of the current working tree's lines group with an "xx"-tagged entry. The -o option eliminates the display of lines from other lines groups.

The -a option may be used to turn off the lines groups feature altogether. This can be useful when, working within a tree representing one of multiple lines groups, you wish to propagate a change to lines outside of the group.

CVSLINES AND REMOTE CVS
cvslines will work properly in "remote cvs" mode, using the "ext" method. Currently, cvslines does not support remote cvs with authentication.

CVSLINES HACKERY
Occassionally, you may want to do a commit specifically to some other line than the one represented by the tree you are working in. (I.e., a commit only to that line, instead of the working tree's line plus some other(s)). This situation often arises when you've deferred the reconcilation of merge conflicts discovered during a previous cvslines commit.

To do this, you can use the following trick, which allows you to change the "current" line of development for a single file within a tree originally checked out from another.

In this example, assume that the tree we're working in is using the line of development on the head of the RCS trunk, and suppose that we want to resolve merge conflicts detected during an attempt to merge changes to "fileX" into the R1_1_BRANCH version, and which we chose not to resolve during that commit. (cvslines has therefore saved the partially merged file as "fileX-conflicts-R1_1_BRANCH").

1.  Remove the existing version of the file, and use 'cvs update -A|-r branchtag' to move the file on to the other line. (Use '-A' if the other line is the head of the RCS trunk, or '-r branchtag, otherwise). E.g.,
       % rm -f fileX; cvs update -r R1_1_BRANCH fileX
       cvs update: warning: fileX was lost
       U fileX
    (At this point, CVS and cvslines will behave as if this file has been checked out into an R1_1_BRANCH tree).

2.  Next, edit the file to make it as you wish to commit it. In our example, the previous commit had saved the partially merged file, so we use:
      % mv fileX-conflicts_R1_1_BRANCH fileX
      % edit fileX to resolve the merge conflicts
      % cvslines commit fileX

3.  Finally, be sure to put the file back into the proper line represented by the working tree:
      % rm -f fileX cvs update -A fileX
      cvs update: warning: fileX was lost
      U fileX

CVSLINES ACTION CATALOG
These are the actions that cvslines may propose for a given line:

update with this commit
    The line will be updated with a "natural" commit, i.e., the commit that would happen in this instance without any intervention by cvslines.

update with a branch tag create
    The line will be updated with the creation of a new branch tag. (Used when new files are added to a branch line).

update with a branch tag update
    The line will be updated by moving an existing branch tag up to a new revision.

update with a revision merge and commit
    The line will be updated by merging the changes represented by the new revision into the tip of an existing branch, and then committing them.

update with a commit
    This line will be updated with a commit (other than the "natural" commit.) This is used for adding new files onto lines on the trunk, when done from a working tree which represents a branched line.

STRUCTURE
cvslines is written in perl. In order to minimize the perl compilation times for the "status", "check" and "commit" subcommands, the code is broken up into a common module, and three subcommand-specific modules that are "require"ed as needed. These must all exist in the same directory.

Also, a symbolic link "cvslines_check -> cvslines" needs to exist. This allows cvslines to be invoked by the standard CVS commitinfo file hook, to run the "check" subcommand.

BUGS
cvslines currently doesn't do "cvs remove"s. You have to make sure they happen in all applicable lines "by hand".

FILES
$CVSROOT/CVSROOT/commitinfo
$CVSROOT/module/cvslines.config
/usr/local/bin/cvslines
/usr/local/bin/cvslines-status
/usr/local/bin/cvslines-commit
/usr/local/bin/cvslines-check
/usr/local/bin/cvslines_check
$HOME/.cvslinesrc
$Id: cvslines.1,v 1.19 1997/02/04 00:02:03 rmg Exp $