package VCP::Help; %topics = ( ######################################################################## 'vcp' => <<'TOPIC', NAME vcp - Copy versions of files between repositories and/or RevML SYNOPSIS # interactive mode: vcp [vcp_opts] # scriptable command line mode: vcp [vcp_opts] # getting options from a file: vcp vcp:config.vcp # help output: vcp help vcp help [topic] DESCRIPTION "vcp" ('version copy') copies versions of files from one repository to another, translating as much metadata as possible along the way. This allows you to copy and translate files and their histories between revision storage systems. Supported source and destination types are "cvs:", "p4:", and "revml:". Copying Versions The general syntax of the vcp command line is: vcp [] The three portions of the command line are: "" Command line options that control the operation of the "vcp" command, like "-d" for debugging or "-h" for help. There are very few global options, these are covered below. Note that they must come before the "" specification. "" Were to extract versions from, including any command line options needed to control what is extracted and how. See the next section. "" Where to insert versions, including any command line options needed to control how files are stored. See the next section. Specifying Repositories The "" and "" specifications specify a repository and provide any options needed for accessing that repository. These spefications may be a simple filename for reading or writing RevML files (if the requisite XML handling modules are installed). or a full repository specification like "cvs:/home/cvs/root:module" or "p4:user:password@server:port://depot/dir". When using the long form to access a repository, "" and "" specification have several fields delimited by ":" and "@", and may have trailing command line options. The full (rarely used) syntax is: scheme:user(view):password@repository:filespec [] where "scheme:" The repository type ("p4:", "cvs:", "revml:"). "user", "view", and "password" Optional values for authenticating with the repository and identifying which view to use. "cvs" does not use "view". For "p4", "view" is the client setting (equibalent to setting "P4CLIENT" or using "p4"'s "-c" option). "repository" The repository spec, CVSROOT for CVS or P4PORT for p4. "filespec" Which versions of what files to move. As much as possible, this spec is similar to the native filespecs used by the repository indicated by the scheme. "" Command line options that usually mimic the options provided by the underlying repositories' command line tools ("cvs", "p4", etc). Most of these fields are omitted in practice, only the "scheme" field is required, though (in most cases) the "repository" field is also needed unless you set the appropriate environment variables ("CVSROOT", "P4PORT", etc). The a bit confusing, here are some examples specs: cvs:server:/foo p4:user@server://depot/foo/... p4:user:password@public.perforce.com:1666://depot/foo/... Options and formats for of individual schemes can be found in the relevant help topics, for instance: vcp help source::cvs Run "vcp help" for a list of topics. When reading and writing RevML files, a simple filename will do (although the long form may also be used). The special value "-" means to read/write stdin and stdout when used as a source or destination name, respectively. "-" is assumed if a specification is not provided, so these invocations all accomplish the same thing, reading and writing RevML: vcp vcp - vcp revml:- vcp revml: vcp - - vcp - revml:- vcp - revml: vcp revml:- revml:- vcp revml: revml: "vcp" Options All general options to vcp must precede the "". Scheme-specific options must be placed immediately after the "" or "" spec and before the next one. --debug, -d Enables logging of debugging information. --help, -h, -? These are all equivalent to "vcp help". --output-config-file=$filename Write the settings (parsed from the UI, the command line, or a config file to a file. Useful for capturing settings or user interface output. Does not affect running. Use "-" to emit to STDOUT. NOTE 1: This does *not* emit an "Options:" section containing global options (those listed here). Almost all of these options are not useful to emit; we can add an option to force their emission if need be. NOTE 2: When using the interactive user interface, this option takes effect after the last interactive portion and, if vcp goes on to run a conversion, before any conversion is run. This occurs in addition to any configuration files the user may ask the interactive interface to write. This may change in the future (for instance, if the interactive dialog includes an option to extract and analyze metadata). --dont-convert Do not run a conversion. Useful when you just want to emit a .vcp file. --versions Emits the version numbers of bundled files. --terse, -t Suppress verbose explanations when running the interactive UI. Has no effect on operation if all settings are read from the command line or a .vcp file. --quiet, -q Suppresses the banner and progress bars. Getting help (See also Generating HTML Documentation, below). There is a slightly different command line format for requesting help: vcp help [] where "" is the optional name of a topic. "vcp help" without a "<"topic">" prints out a list of topics, and "vcp help vcp" emits this page. All help documents are also available as Unix "man" pages and using the "perldoc" command, although the names are slightly different: with vcp via perldoc ================ =========== vcp help vcp perldoc vcp vcp help source::cvs perldoc VCP::Source::cvs vcp help source::cvs perldoc VCP::Dest::p4 "vcp help" is case insensitive, "perldoc" and "man" may or may not be depending on your filesystem. The "man" commands look just like the example "perldoc" commands except for the command name. Both have the advantage that they use your system's configured pager if possible. Environment Variables The environment is often used to set context for the source and destination by way of variables like P4USER, P4CLIENT, CVSROOT, etc. VCPDEBUG The VCPDEBUG variable acts just like "-d=$VCPDEBUG" was present on the command line: VCPDEBUG=1 (see "--debug, -d" for more info). This is useful when VCP is embedded in another application, like a makefile or a test suite. SEE ALSO VCP::Process, VCP::Newlines, VCP::Source::p4, VCP::Dest::p4, VCP::Source::cvs, VCP::Dest::cvs, VCP::Source::revml, VCP::Dest::revml, VCP::Newlines. All are also available using "vcp help". AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'vcp usage' => <<'TOPIC', Usage: # interactive mode: vcp [vcp_opts] # scriptable command line mode: vcp [vcp_opts] # getting options from a file: vcp vcp:config.vcp # help output: vcp help vcp help [topic] TOPIC ######################################################################## 'filter' => <<'TOPIC', NAME VCP::Filter - A base class for filters SYNOPSIS use VCP::Filter; @ISA = qw( VCP::Filter ); ... DESCRIPTION A VPC::Filter is a VCP::Plugin that is placed between the source and the destination and allows the stream of revisions to be altered. For instance, the Map: option in vcp files is implemented by VCP::Filter::Map By default a filter is a pass-through. SUBCLASSING This class uses the fields pragma, so you'll need to use base and possibly fields in any subclasses. parse_rules_list Used in VCP::Filter::*map and VCP::Filter::*edit to parse lists of rules where every rule is a set of N "words". The value of N is computed from the number of labels passed in and the labels are used when printing an error message: @rules = $self->parse_rules( $options, "Pattern", "Replacement" ); filter_name Returns the StudlyCaps version of the filter name. By default, assumes a single work name and uses ucfirst on it. Filters like StringEdit should overload this to be more creative and typgraphically appealing (heh). sort_keys my @output_sort_order = $filter->sort_keys( @input_sort_order ); Accepts a list of sort keys from the upstream filter and returns a list of sort keys representing the order that records will be emitted in. This is a pass-through by default, but VCP::Filter::sort and VCP::Filter::changesets return appropriate values. config_file_section_as_string last_rev_in_filebranch (passthru; see VCP::Dest) backfill (passthru; see VCP::Dest) handle_header (passthru) rev_count $self->SUPER::rev_count( @_ ); passthru, see VCP::Dest. handle_rev $self->SUPER::handle_rev( @_ ); passthru, see VCP::Dest. skip_rev $self->SUPER::skip_rev( @_ ); passthru, see VCP::Dest handle_footer $self->SUPER::handle_footer( @_ ); passthru, see VCP::Dest COPYRIGHT Copyright 2000, Perforce Software, Inc. All Rights Reserved. This module and the VCP package are licensed according to the terms given in the file LICENSE accompanying this distribution, a copy of which is included in vcp. AUTHOR Barrie Slaymaker TOPIC ######################################################################## 'source' => <<'TOPIC', NAME VCP::Source - A base class for repository sources SYNOPSIS DESCRIPTION OPTIONS --bootstrap --bootstrap=pattern Forces all files matching the given shell regular expression (may use wildcards like "*", "?", and "...") to have their first revisions transferred as complete copies instead of deltas. This is useful when you want to transfer a revision other than the first revision as the first revision in the target repository. It is also useful when you want to skip some revisions in the target repository (although the Map filter has superceded this use). --continue Tells VCP to continue where it left off from last time. This will not detect new branches of already transferred revisions (this limitation should be lifted, but results in an expensive rescan of metadata), but will detect updates to already transferred revisions. --rev-root Tells VCP to extract files relative to a directory in the source repository other than the default directory. Ordinarily, VCP looks at the source specification and deduces that the lowest complete directory name is the "root" directory for all revisions, or the "rev_root". For instance, given the specification: cvs:foo/bar/... the rev_root will be "foo/bar" and the files under bar will be extracted with a path relative to bar, so "foo/bar/baz/bat" will be extracted with the value "baz/bat". They will be inserted in the destination repository relative to the rev_root for the destination, so if the destination spec is like: p4://depot/... then "baz/bat" will be inserted in the destination as "//depot/baz/bat". If there is no target rev_root specified, as in the spec: p4: then the source's rev_root will be assumed, so "baz/bat" in our example would be placed in "//foo/bar/baz/bat". SUBCLASSING This class uses the fields pragma, so you'll need to use base and possibly fields in any subclasses. See VCP::Plugin for methods often needed in subclasses. Subclass utility API options_spec Adds common VCP::Source options to whatever options VCP::Plugin parses: dest Sets/Gets a reference to the VCP::Dest object. The source uses this to call handle_header(), handle_rev(), and handle_end() methods. continue Sets/Gets the CONTINUE field (which the user sets via the --continue flag) real_source Returns the reference to be used when sending revisions to the destination. Each revision has a pointer to the source that sends it so that filters and destinations can call get_source_file(). Most sources return $self; Sources that spool data, such as VCP::Source::metadb, need to specify a real source. They do so by overloading this method. VCP::Source::revml does not do this, as it supplies a get_source_file(). rev_mode my $mode = $self->rev_mode( $filebranch_id, $rev_id ); Returns FALSE, "base", or "normal" as a function of the filebranch and rev_id. Do not queue the revision if this returns FALSE (you may also skip any preceding revisions). Queue it only as a base revision if it returns "base", and queue it as a full revision otherwise. Not all base revs will be sent; base revs that have no child revs will not be sent. Always returns "normal" when not in continue mode. queue_rev Some revs can't be sent immediately. They get queued. Once queued, the revision may not be altered. All revisions must be queued before being sent. All revs from the source repository should be queued, --continue processing is automatic. Placeholders should be inserted for all branches, even empty ones. This updates last_rev and last_rev_for_filebranch. Returns FALSE if the rev cannot be queued, for instance if it's already been queued once. rev_mode() should be called before creating a rev, or at least before queue_rev()ing it in order to see if and in what form the rev should be sent. queued_rev $self->queued_rev( $id ); Returns a queued rev by id. Sources where revs can arrive willy-nilly, like VCP::Source::revml, queue up all revs and need to randomly access them. last_rev_for_filebranch $self->last_rev_for_filebranch( $filebranch_id ); Returns the last revision queued on the indicated filebranch. set_last_rev_in_filebranch_previous_id $self->set_last_rev_in_filebranch_previous_id( $r ); If there is a last_rev_for_filebranch for $r->filebranch_id, sets its previous_id to point to $r. This is useful for sources which scan in most-recent-first order. queued_rev_count Returns (does not set) the number of revs queued so far. Replaces the deprecated function sent_rev_count(). store_cached_revs $self->store_cached_revs; For parsers which read history one file at a time and branch in rev_id space, like VCP::Source::cvs, it's possible to flush all revs to disk after each file is parsed. This method takes the last VCP::Rev in each filebranch and stores it to disk, freeing memory. send_revs $self->send_revs; Removes and sends all revs accumulated so far. Called automatically after scan_metadata(). SUBCLASS OVERLOADS These methods should be overridded in any subclasses. scan_metadata This is called to scan the metadata for the source repository. It should call rev_mode() for each revision found (including any that need to be concocted to make up for collapsed metadata in the source, like VSS or CVS deletes or CVS branch creation) and if that returns TRUE, then queue_rev() should be called. If rev_mode() returns "base", then the transfer is in --continue mode and this rev should be built as or converted to a base revision. The easiest way to do this is to build it normally and then call $r->base_revify(). If the metadata source returns metadata from most recent to oldest, as do most file history reports, the previous_id() need not be set until the next revision in a filebranch is scanned. The most recent rev passed to queue_rev() is available by calling last_rev(), if the metadata is one branch at a time, and the last rev in each filebranch is available by calling last_rev_for_filebranch(). If the metadata is scanned one file or filebranch at a time and branched are all created by the time the end of a file's metadata arrives, calling store_cached_revs() will flush all queued revs from the last_rev() and last_rev_for_filebranch() in-memory caches to the disk cache (all other revs are flushed as their successors arrive). There is no easy way to handle randomly ordered metadata at this time, typically a source will accumulate as little as it can in memory and queue the rest. See VCP::Source::cvs for an example of this. Once scan_metadata() is complete, send_revs() will be called automatically. get_source_file REQUIRED OVERLOAD. All sources must provide a way for the destination to fetch a revision. handle_header REQUIRED OVERLOAD. Subclasses must add all repository-specific info to the $header, at least including rep_type and rep_desc. $header->{rep_type} => 'p4', $self->p4( ['info'], \$header->{rep_desc} ) ; The subclass must pass the $header on to the dest: $self->dest->handle_header( $header ) if $self->dest; This may be called when dest is null to allow the source to initialize itself when it won't be scanning the real source. So the if $self->dest is important. That's not the case for copy_revs(). handle_footer Not a required overload, as the footer carries no useful information at this time. Overriding methods must call this method to pass the $footer on: $self->SUPER::handle_footer( $footer ) ; parse_time $time = $self->parse_time( $timestr ) ; Parses "[cc]YY/MM/DD[ HH[:MM[:SS]]]". Will add ability to use format strings in future. HH, MM, and SS are assumed to be 0 if not present. Returns a time suitable for feeding to localtime or gmtime. Assumes local system time, so no good for parsing times in revml, but that's not a common thing to need to do, so it's in VCP::Source::revml.pm. bootstrap Sets (and parses) or gets the bootstrap spec. Can be called plain: $self->bootstrap( $bootstrap_spec ) ; See the command line documentation for the format of $bootstrap_spec. is_bootstrap_mode ... if $self->is_bootstrap_mode( $file ) ; Compares the filename passed in against the list of bootstrap regular expressions set by "bootstrap". The file should be in a format similar to the command line spec for whatever repository is passed in, and not relative to rev_root, so "//depot/foo/bar" for p4, or "module/foo/bar" for cvs. This is typically called in the subbase class only after looking at the revision number to see if it is a first revision (in which case the subclass should automatically put it in bootstrap mode). COPYRIGHT Copyright 2000, Perforce Software, Inc. All Rights Reserved. This module and the VCP package are licensed according to the terms given in the file LICENSE accompanying this distribution, a copy of which is included in vcp. AUTHOR Barrie Slaymaker TOPIC ######################################################################## 'defaultfilters' => <<'TOPIC', NAME VCP::DefaultFilters - Class for determining default filters to install for a given source and dest. SYNOPSIS require VCP::DefaultFilters; my $df = VCP::DefaultFilters->new; my @filter_args = $df->create_default_filters( $source, $dest ); DESCRIPTION Given references to a vcp source and destination, determines the default filters which would be appropriate, builds and returns a list of arguments that should look like the portion of @ARGV (command line arguments) that specify filters. COPYRIGHT Copyright 2000, Perforce Software, Inc. All Rights Reserved. This module and the VCP package are licensed according to the terms given in the file LICENSE accompanying this distribution, a copy of which is included in vcp. TOPIC ######################################################################## 'dest' => <<'TOPIC', NAME VCP::Dest - A base class for VCP destinations SYNOPSIS DESCRIPTION SUBCLASS API These methods are intended to support subclasses. digest $self->digest( "/tmp/readers" ) ; Returns the Base64 MD5 digest of the named file. Used to compare a base rev (which is the revision *before* the first one we want to transfer) of a file from the source repo to the existing head rev of a dest repo. The Base64 version is returned because that's what RevML uses and we might want to cross-check with a .revml file when debugging. compare_base_revs $self->compare_base_revs( $rev, $work_path ) ; Checks out the indicated revision from the destination repository and compares it (using digest()) to the file from the source repository (as indicated by $work_path). Dies with an error message if the base revisions do not match. Calls $self->checkout_file( $rev ), which the subclass must implement. header Gets/sets the $header data structure passed to handle_header(). rev_map Returns a reference to the RevMapDB for this backend and repository. Creates an empty one if need be. head_revs Returns a reference to the HeadRevsDB for this backend and repository. Creates an empty one if need be. main_branch_id Returns a reference to the MainBranchIdDB for this backend and repository. Creates an empty one if need be. files Returns a reference to the FilesDB for this backend and repository. Creates an empty one if need be. SUBCLASS OVERLOADS These methods are overloaded by subclasses. backfill $dest->backfill( $rev ) ; Checks the file indicated by VCP::Rev $rev out of the target repository if this destination supports backfilling. Currently, only the revml and the reporting & debugging destinations do not support backfilling. The $rev->workpath must be set to the filename the backfill was put in. This is used when doing an incremental update, where the first revision of a file in the update is encoded as a delta from the prior version. A digest of the prior version is sent along before the first version delta to verify it's presence in the database. So, the source calls backfill(), which returns TRUE on success, FALSE if the destination doesn't support backfilling, and dies if there's an error in procuring the right revision. If FALSE is returned, then the revisions will be sent through with no working path, but will have a delta record. MUST BE OVERRIDDEN. sort_filter sub sort_filter { my $self = shift; my @sort_keys = @_; return () if @sort_keys && $sort_keys[0] eq "change_id"; require VCP::Filter::changesets; return ( VCP::Filter::changesets->new(), ); } This is passed a sort specification string and returns any filters needed to presort data for this destination. It may return the empty list (the default), or one or more instantiated filters. require_change_id_sort Destinations that care about the sort order usually want to use the changesets filter, so they can overload the sort filter like so: sub sort_filters { shift->require_change_id_sort( @_ ) } handle_footer $dest->handle_footer( $footer ) ; Does any cleanup necessary. Not required. Don't call this from the override. handle_header $dest->handle_header( $header ) ; Stows $header in $self->header. This should only rarely be overridden, since the first call to handle_rev() should output any header info. rev_count $dest->rev_count( $number_of_revs_forthcoming ); Sent by the last aggregating plugin in the filter chain just before the first revision is sent to inform us of the number of revs to expect. skip_rev Sent by filters that discard revisions in line. handle_rev $dest->handle_rev( $rev ) ; Outputs the item referred to by VCP::Rev $rev. If this is the first call, then $self->none_seen will be TRUE and any preamble should be emitted. MUST BE OVERRIDDEN. Don't call this from the override. last_rev_in_filebranch my $rev_id = $dest->last_rev_in_filebranch( $source_repo_id, $source_filebranch_id ); Returns the last revision for the file and branch indicated by $source_filebranch_id. This is used to support --continue. Returns undef if not found. NOTES Several fields are jury rigged for "base revisions": these are fake revisions used to start off incremental, non-bootstrap transfers with the MD5 digest of the version that must be the last version in the target repository. Since these are "faked", they don't contain comments or timestamps, so the comment and timestamp fields are treated as "" and 0 by the sort routines. COPYRIGHT Copyright 2000, Perforce Software, Inc. All Rights Reserved. This module and the VCP package are licensed according to the terms given in the file LICENSE accompanying this distribution, a copy of which is included in vcp. AUTHOR Barrie Slaymaker TOPIC ######################################################################## 'filter::csv_trace' => <<'TOPIC', NAME VCP::Filter::csv_trace - developement logging filter DESCRIPTION Dumps fields of revisions in CSV format. Not a supported module, API and behavior may change without warning. AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::csv_trace usage' => <<'TOPIC', TOPIC ######################################################################## 'filter::csv_trace description' => <<'TOPIC', Dumps fields of revisions in CSV format. Not a supported module, API and behavior may change without warning. TOPIC ######################################################################## 'filter::logmemsize' => <<'TOPIC', NAME VCP::Filter::logmemsize - developement logging filter DESCRIPTION Watches memory size. Only works on linux for now. Not a supported module, API and behavior may change without warning. AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::logmemsize usage' => <<'TOPIC', TOPIC ######################################################################## 'filter::logmemsize description' => <<'TOPIC', Watches memory size. Only works on linux for now. Not a supported module, API and behavior may change without warning. TOPIC ######################################################################## 'filter::changesets' => <<'TOPIC', NAME VCP::Filter::changesets - Group revs in to changesets SYNOPSIS ## From the command line: vcp changesets: ...options... -- ## In a .vcp file: ChangeSets: time <=60 ## seconds user_id equal ## case-sensitive equality comment equal ## case-sensitive equality source_filebranch_id notequal ## case-sensitive inequality DESCRIPTION This filter is automatically loaded when there is no sort filter loaded (both this and VCP::Filter::sort count as sort filters). Sorting by change_id, etc. When all revs from the source have change numbers, this filter sorts by change_id, branch_id, and name, regardless of the rules set. The name sort is case sensitive, though it should not be for Win32. This sort by change_id is necessary for sources that supply change_id because the order of scanning the revisions is not usually (ever, so far :) in change set order. Aggregating changes If one or more revisions arrives from the source with an empty change_id, the rules for this filter establish the conditions that determine what revisions may be grouped in to each change. In this case, this filter rewrites all change_id fields so that the (eventual) destination can use the change_id field to break the revisions in to changes. This is sometimes used by non-changeset oriented destinations to aggregate "changes" as though a user were performing them and to reduce the number of individual operations the destination driver must perform (for instance: VCP::Dest::cvs prefers to not call cvs commit all the time; cvs commit is slow). Revisions are aggregated in to changes using a set of rules that determine what revisions may be combined. One rule is implicit in the algorithm, the others are explicitly specified as a set of defaults that may be altered by the user. The Implicit Rule The implicit rule is that no change may contain two revisions where one is a descendant of another. The algorithm starts with the set of revisions that have no parents in this transfer, chooses a set of them to be a change according to the explicit conditions, and emits it. Only when a revision is emitted does this filter consider it's offspring for emission. This cannot be changed. (EXPERIMENTAL) The only time this implicit rule is not enough is in a cloning situation. In CVS and VSS, it is possible to "share" files between branches. VSS supports and promotes this model in its user interface and documentation while CVS allows it more subtlely by allowing the same branch to have multiple branch tags. In either case, there are multiple branches of a file that are changed simultaneously. The CVS source recognizes this (and the VSS source may by the time you read this) and chooses a master revision from which to "clone" other revisions. These cloned revisions appear on the child branch as children of the master revision, not as children of the preceding revision on the child branch. This is confusing, but it works. In order to prevent this from confusing the destinations, however, it can be important to make sure that two revisions to a given branch of a given file do not occur in the same revision; this is the purpose of the explicit rule "source_filebranch_id notequal", covered below. The Explicit Rules Rules may be specified for the ChangeSets filter. If no rules are specified, a set of default rules are used. If any rules are specified, none of the default rules are used. The default rules are explained after rule conditions are explained. Each rule is a pair of words: a data field and a condition. There are three conditions: "notequal", "equal" and "<=N" (where N is a number; note that no spaces are allowed before the number unless the spec is quoted somehow): equal The "equal" condition is valid for all fields and states that all revisions in the same change must have identical values for the indicated field. So: user_id equal states that all revisions in a change must be submitted by the same user. All "equal" conditions are used before any other conditions, regardless of the order they are specified in to categorize revisions in to prototype changes. Once all revisions have been categorized in to prototyps changes, the "<=N" and "notequal" rules are applied in order to split the change prototypes in to as many changes as are needed to satisfy them. notequal The "notequal" condition is also valid for all fields and specifies that no two revisions in a change may have equal values for a field. It does not make sense to apply this to time fields, and is usually only needed to ensure that two revisions to the same file on the same branch do not get bundled in to the same change. <=N The "<=N" specification is only available for the "time" field. It specifices that no gaps larger than N seconds may exist in a change. The default rules are: time <=60 ## seconds user_id equal ## case-sensitive equality comment equal ## case-sensitive equality source_filebranch_id notequal ## case-sensitive inequality These rules The "time <=60" condition sets a maximum allowable difference between two revisions; revisions that are more than this number of seconds apart are considered to be in different changes. The "user_id equal" and "comment equal" conditions assert that two revisions must be by the same user and have the same comment in order to be in the same change. The "source_filebranch_id notequal" condition prevents cloned revs of a file from appearing in the same change as eachother (see the discussion above for more details). ALGORITHM handle_rev() As revs are received by handle_rev(), they are store on disk. Several RAM-efficient (well, for Perl) data structures are built, however, that describe each revision's children and its membership in a changeset. Some or all of these structures may be moved to disk when we need to handly truly large data sets. The ALL_HAVE_CHANGE_IDS statistic One statistic that handle_rev() gathers is whether or not all revisions arrived with a non-empty change_id field. The REV_COUNT statistic How many revisions have been recieved. This is used only for UI feedback; primarily it is to forewarn the downstream filter(s) and destination of how many revisions will constitute a 100% complete transfer. The CHANGES list As each rev arrives, it is placed in a "protochange" determined solely by the revision's fields in the rules list with an "equal" condition. Protochanges are likely to have too many revisions in them, including revisions that descend from one another and revisions that are too far apart in time. The CHANGES_BY_KEY index The categorization of each revision in to changes is done by forming a key string from all the fields in the rules list with the "equal" condition. This index maps unique keys to changes. The CHILDREN index This is an index of all revisions that are direct offspring of a revision. the PREDECESSOR_COUNT statistic Counts the number of parents a revision has that haven't been submitted yet. A revision may have a previous_id and, optionally, also have a from_id (can't have a from_id without a previous_id, however). The REVS_BY_CHANGE_ID index If all revs do indeed arrive with change_ids, they need to be sorted and sent out in order. This index is gathered until the first rev with an empty change_id arrives. The ROOT_IDS list This is a list of the IDs of all revisions that have no parent revisions in this transfer. This is used as the starting point for send_changes(), below. The CHANGES_BY_REV index As the large protochanges are split in to smaller ones, the resulting CHANGES list is indexed by, among other things, which revs are in the change. This is so the algorithms can quickly find what change a revision is in when it's time to consider sending that revision. handle_footer() All the real work occurs when handle_footer() is called. handle_footer() glances at the change_id statistic gathered by handle_rev() and determines whether it can sort by change_id or whether it has to perform change aggregation. If all revisions arrive with a change_id, sort_by_change_id_and_send() If at least one revision didn't handle_footer() decides to perform change aggregation by calling split_protochanges() and then send_changes(). Any source or upstream filter may perform change aggregation by assigning change_ids to all revisions. VCP::Source::p4 does this. At the time of this writing no otherd do. Likewise, a filter like VCP::Filter::StringEdit may be used to clear out all the change_ids and force change aggregation. sort_by_change_id_and_send() If all revisions arrived with a change_id, then they will be sorted by the values of ( change_id, time, branch_id, name ) and sent on. There is no provision in this filter for ignoring change_id other than if any revisions arrive with an empty change_id, this sort is not done. split_and_send_changes() Once all revisions have been placed in to protochanges, a change is selected and sent like so: 1 Get an oldest change with no revs that can't yet be sent. If none is found, then select one oldest change and remove any revs that can't be sent yet. 2 Select as many revs as can legally be sent in a change by sorting them in to time order and then using the <=N and notequal rules to determine if each rev can be sent given the revs that have already passed the rules. Delay all other revs for a later change. LIMITATIONS This filter does not take the source_repo_id in to account: if somehow you are merging multiple repositories in to one and want to interleave the commits/submits "properly", ask for advice. AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::changesets usage' => <<'TOPIC', Usage: ## From the command line: vcp changesets: ...options... -- ## In a .vcp file: ChangeSets: time <=60 ## seconds user_id equal ## case-sensitive equality comment equal ## case-sensitive equality source_filebranch_id notequal ## case-sensitive inequality TOPIC ######################################################################## 'filter::changesets description' => <<'TOPIC', This filter is automatically loaded when there is no sort filter loaded (both this and VCP::Filter::sort count as sort filters). Sorting by change_id, etc. ========================== When all revs from the source have change numbers, this filter sorts by change_id, branch_id, and name, regardless of the rules set. The name sort is case sensitive, though it should not be for Win32. This sort by change_id is necessary for sources that supply change_id because the order of scanning the revisions is not usually (ever, so far :) in change set order. Aggregating changes =================== If one or more revisions arrives from the source with an empty change_id, the rules for this filter establish the conditions that determine what revisions may be grouped in to each change. In this case, this filter rewrites all change_id fields so that the (eventual) destination can use the change_id field to break the revisions in to changes. This is sometimes used by non-changeset oriented destinations to aggregate "changes" as though a user were performing them and to reduce the number of individual operations the destination driver must perform (for instance: VCP::Dest::cvs prefers to not call cvs commit all the time; cvs commit is slow). Revisions are aggregated in to changes using a set of rules that determine what revisions may be combined. One rule is implicit in the algorithm, the others are explicitly specified as a set of defaults that may be altered by the user. The Implicit Rule ================= The implicit rule is that no change may contain two revisions where one is a descendant of another. The algorithm starts with the set of revisions that have no parents in this transfer, chooses a set of them to be a change according to the explicit conditions, and emits it. Only when a revision is emitted does this filter consider it's offspring for emission. This cannot be changed. (EXPERIMENTAL) The only time this implicit rule is not enough is in a cloning situation. In CVS and VSS, it is possible to "share" files between branches. VSS supports and promotes this model in its user interface and documentation while CVS allows it more subtlely by allowing the same branch to have multiple branch tags. In either case, there are multiple branches of a file that are changed simultaneously. The CVS source recognizes this (and the VSS source may by the time you read this) and chooses a master revision from which to "clone" other revisions. These cloned revisions appear on the child branch as children of the master revision, not as children of the preceding revision on the child branch. This is confusing, but it works. In order to prevent this from confusing the destinations, however, it can be important to make sure that two revisions to a given branch of a given file do not occur in the same revision; this is the purpose of the explicit rule "source_filebranch_id notequal", covered below. The Explicit Rules ================== Rules may be specified for the ChangeSets filter. If no rules are specified, a set of default rules are used. If any rules are specified, none of the default rules are used. The default rules are explained after rule conditions are explained. Each rule is a pair of words: a data field and a condition. There are three conditions: "notequal", "equal" and "<=N" (where N is a number; note that no spaces are allowed before the number unless the spec is quoted somehow): equal ===== The "equal" condition is valid for all fields and states that all revisions in the same change must have identical values for the indicated field. So: user_id equal states that all revisions in a change must be submitted by the same user. All "equal" conditions are used before any other conditions, regardless of the order they are specified in to categorize revisions in to prototype changes. Once all revisions have been categorized in to prototyps changes, the "<=N" and "notequal" rules are applied in order to split the change prototypes in to as many changes as are needed to satisfy them. notequal ======== The "notequal" condition is also valid for all fields and specifies that no two revisions in a change may have equal values for a field. It does not make sense to apply this to time fields, and is usually only needed to ensure that two revisions to the same file on the same branch do not get bundled in to the same change. <=N === The "<=N" specification is only available for the "time" field. It specifices that no gaps larger than N seconds may exist in a change. The default rules are: time <=60 ## seconds user_id equal ## case-sensitive equality comment equal ## case-sensitive equality source_filebranch_id notequal ## case-sensitive inequality These rules The "time <=60" condition sets a maximum allowable difference between two revisions; revisions that are more than this number of seconds apart are considered to be in different changes. The "user_id equal" and "comment equal" conditions assert that two revisions must be by the same user and have the same comment in order to be in the same change. foo === The "branched_rev_branch_id equal" condition is a special case to handle repositories like CVS which don't record branch creation times. This condition kicks in when a user creates several branches before changing any files on any of them; in this case all of the branches get created at the same time. That leaves odd looking conversions. This condition also kicks in when multiple CVS branches exist with no changes on them. In this case, VCP::Source::cvs groups all of the branch creations after the last "real" edit. In both cases, the changeset filter splits branch creations so that only one branch is created per change. The "branched_rev_branch_id" condition only applies to revisions branching from one branch in to another. foo === The "source_filebranch_id notequal" condition prevents cloned revs of a file from appearing in the same change as eachother (see the discussion above for more details). TOPIC ######################################################################## 'filter::sort' => <<'TOPIC', NAME VCP::Filter::sort - Sort revs by field, order SYNOPSIS ## From the command line: vcp sort: name ascending rev_id ascending -- ## In a .vcp file: Sort: name ascending rev_id ascending DESCRIPTION NOTE: this filter is primarily for development and testing, it is not designed for large datasets (it can use a lot of RAM if fed enough data). Useful with the revml: destination to get RevML output in a desired order. Otherwise the sorting built in to the change aggregator should suffice. The default sort spec is "name,rev_id" which is what is handy to VCP's test suite as it puts all revisions in a predictable order so the output revml can be compared to the input revml. NOTE: this is primarily for development use; not all fields may work right. All plain string fields should work right as well as name, rev_id, change_id and their source_... equivalents (which are parsed and compared piece-wise) and time, and mod_tome (which are stored as integers internally). Plain case sensitive string comparison is used for all fields other than those mentioned in the preceding paragraphs. This sort may be slow for extremely large data sets; it sorts things by comparing revs to eachother field by field instead of by generating indexes and VCP::Rev is not designed to be super fast when accessing fields one by one. This can be altered if need be. How rev_id and change_id are sorted "change_id" or "rev_id" are split in to segments suitable for sorting. The splits occur at the following points: 1. Before and after each substring of consecutive digits 2. Before and after each substring of consecutive letters 3. Before and after each non-alpha-numeric character The substrings are greedy: each is as long as possible and non-alphanumeric characters are discarded. So "11..22aa33" is split in to 5 segments: ( 11, "", 22, "aa", 33 ). If a segment is numeric, it is left padded with 10 NUL characters. This algorithm makes 1.52 be treated like revision 1, minor revision 52, not like a floating point 1.52. So the following sort order is maintained: 1.0 1.0b1 1.0b2 1.0b10 1.0c 1.1 1.2 1.10 1.11 1.12 The substring "pre" might be treated specially at some point. (At least) the following cases are not handled by this algorithm: 1. floating point rev_ids: 1.0, 1.1, 1.11, 1.12, 1.2 2. letters as "prereleases": 1.0a, 1.0b, 1.0, 1.1a, 1.1 LIMITATIONS Stores all metadata in RAM. AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::sort usage' => <<'TOPIC', Usage: ## From the command line: vcp sort: name ascending rev_id ascending -- ## In a .vcp file: Sort: name ascending rev_id ascending TOPIC ######################################################################## 'filter::sort description' => <<'TOPIC', NOTE: this filter is primarily for development and testing, it is not designed for large datasets (it can use a lot of RAM if fed enough data). Useful with the revml: destination to get RevML output in a desired order. Otherwise the sorting built in to the change aggregator should suffice. The default sort spec is "name,rev_id" which is what is handy to VCP's test suite as it puts all revisions in a predictable order so the output revml can be compared to the input revml. NOTE: this is primarily for development use; not all fields may work right. All plain string fields should work right as well as name, rev_id, change_id and their source_... equivalents (which are parsed and compared piece-wise) and time, and mod_tome (which are stored as integers internally). Plain case sensitive string comparison is used for all fields other than those mentioned in the preceding paragraphs. This sort may be slow for extremely large data sets; it sorts things by comparing revs to eachother field by field instead of by generating indexes and VCP::Rev is not designed to be super fast when accessing fields one by one. This can be altered if need be. TOPIC ######################################################################## 'filter::identity' => <<'TOPIC', NAME VCP::Filter::identity - identity (ie noop) SYNOPSIS vcp identity: DESCRIPTION A simple passthrough, used for testing to make sure that VCP::Filter really is a pass through and that vcp can load filters. AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::identity usage' => <<'TOPIC', Usage: vcp identity: TOPIC ######################################################################## 'filter::identity description' => <<'TOPIC', A simple passthrough, used for testing to make sure that VCP::Filter really is a pass through and that vcp can load filters. test_script t/10vcp.t ===================== TOPIC ######################################################################## 'filter::dumpdata' => <<'TOPIC', NAME VCP::Filter::dumpdata - developement output filter DESCRIPTION Dump all data structures. Requires the module BFD, which is not installed automatically. Dumps to the log file. Not a supported module, API and behavior may change without warning. AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::dumpdata usage' => <<'TOPIC', TOPIC ######################################################################## 'filter::dumpdata description' => <<'TOPIC', Dump all data structures. Requires the module BFD, which is not installed automatically. Dumps to the log file. Not a supported module, API and behavior may change without warning. TOPIC ######################################################################## 'filter::addlabels' => <<'TOPIC', NAME VCP::Filter::addlabels - Add labels to each revision SYNOPSIS ## From the command line: vcp addlabels: "rev_$rev_id" "change_$change_id" -- ## In a .vcp file: AddLabels: rev_$rev_id change_$change_id # ... etc ... DESCRIPTION Used when you want to track the original rev_id, change_id, branch_id, etc. each revision had in the source repository by adding a label. Can be used to turn any piece of metadata in to a label. Note that the fields source_name, source_filebranch_id, source_branch_id, source_rev_id, source_change_id are set by VCP to be the same value as the corresponding fields without the source prefix (except source_filebranch_id, which is built from the file name, rooted in the repository, and for cvs repositories, the branch number in angle brackets.) These source_* fields (intended to be immutable in vcp) should be used to make labels rather than their mutable equivalents which may be changed via a vcp filter. There is no way to add labels only to selected revisions at this time, but if you try to add a label for metadata that is undefined or empty, it will not be added. AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::addlabels usage' => <<'TOPIC', Usage: ## From the command line: vcp addlabels: "rev_$rev_id" "change_$change_id" -- ## In a .vcp file: AddLabels: rev_$rev_id change_$change_id # ... etc ... TOPIC ######################################################################## 'filter::addlabels description' => <<'TOPIC', Used when you want to track the original rev_id, change_id, branch_id, etc. each revision had in the source repository by adding a label. Can be used to turn any piece of metadata in to a label. Note that the fields source_name, source_filebranch_id, source_branch_id, source_rev_id, source_change_id are set by VCP to be the same value as the corresponding fields without the source prefix (except source_filebranch_id, which is built from the file name, rooted in the repository, and for cvs repositories, the branch number in angle brackets.) These source_* fields (intended to be immutable in vcp) should be used to make labels rather than their mutable equivalents which may be changed via a vcp filter. There is no way to add labels only to selected revisions at this time, but if you try to add a label for metadata that is undefined or empty, it will not be added. test_script t/61addlabels.t =========================== TOPIC ######################################################################## 'filter::stringedit' => <<'TOPIC', NAME VCP::Filter::stringedit - alter any field character by character SYNOPSIS StringEdit: ## Convert illegal p4 characters to ^NN hex escapes and the ## p4 wildcard "..." to a safe string. The "^" is not an illegal ## char, it's replaced with an escape to allow us to use it as ## an escape character without the (extremely small) risk of ## running across a file name that actually uses it. ## Order is significant in this ruleset. # field(s) match replacement name,labels /([\s@#*%^])/ ^%02x name,labels "..." ^___ StringEdit: ## underscorify each unwanted character to a single "_" name,labels /[\s@#*%^]/ _ StringEdit: ## underscorify each run of unwanted characters to a single "_" name,labels /[\s@#*%^]*/ _ StringEdit: ## prefix labels that don't start with a letter or underscore: labels /([^a-zA-Z_])/ _%c DESCRIPTION Allows field by field string editing, using Perl regular expressions to match characters and substrings and sprintf-like replacement strings. Rules A rule is a triplet of expressions specifying a (1) set of fields to match, (2) a pattern to match against those fields' contents (matching contents are removed), and (3) a string to replace each of the removed bits with. NOTE 1: the "match" expression uses perl5 regular expressions, not filename wildcards used in most other places in VCP configurations. The list of rules is evaluated top down and all rules are applied to each string. NOTE 2: The all-rules-apply nature of this filter is different from the behaviors of the ...Map: filters, which stop after the first matching rule. This is because ...Map: filters are rewriting entire strings and there can be only one result string, while the StringEdit filter may be rewriting pieces of string and multiple rewrites may be combined to good effect. The Fields List A comma separated list of field names. Any field may be edited except those that begin with "source_". The Match Expression For each field, the match expression is run against the field and, if it matches, causes all matching portions of string to be replaced. The match expression is a full perl5 regular expression enclosed in /.../ delimiters or a plain string, either of which may be enclosed in '' or "" delimiters if inline spaces are needed (rare, we hope). The Replacement Expression Each match is replaced by one instance of the replacement expression, optionally enclosed in single or double quotation marks. The replacement expression provides a limited list of C sprintf style macros: %d The decimal codes for each character in the match %o The octal codes for each character in the match %x The hex codes for each character in the match Any non-letter preceded by a backslash "\" character is replaced by itself. Some more or less useful examples: \% \\ \" \' \` \{ \} \$ \* \+ \? \1 If a punctuation character other than a period (.) or slash "/" follows a letter macro, it must be escaped using the backslash character (this is to reserve room in the spec for postfix modifiers like "*", "+", and "?"). So, to put a literal star (*) after a hex code, you would do something like "%02x\*". The "normal" perl5 letter abbreviations are also allowed: \t tab (HT, TAB) \n newline (NL) \r return (CR) \f form feed (FF) \b backspace (BS) \a alarm (bell) (BEL) \e escape (ESC) \033 octal char (ESC) \x1b hex char (ESC) \x{263a} wide hex char (SMILEY) \c[ control char (ESC) \N{name} named Unicode character including the following escape sequences are available in constructs that modify what follows: \l lowercase next char \u uppercase next char \L lowercase till \E \U uppercase till \E \E end case modification \Q quote non-word characters till \E As shown above, normal sprintf-style options may be included (and are recommended), so %02x produces results like "%09" (if the match was a single TAB character) or "%20" (if the match was a SPACE character). The dot precision modifiers (".3") are not supported, just the leading 0 and the field width specifier. Case sensitivity By default, all patterns are case sensitive. There is no way to override this at present; one will be added. Command Line Parsing For large stringedits or repeated use, the stringedit is best specified in a .vcp file. For quick one-offs or scripted situations, however, the stringedit: scheme may be used on the command line. In this case, each parameter is a "word" and every triple of words is a ( pattern, result ) pair. Because vcp command line parsing is performed incrementally and the next filter or destination specifications can look exactly like a pattern or result, the special token "--" is used to terminate the list of patterns if StringEdit: is used on the command line. This may also be the last word in the "StringEdit:" section of a .vcp file, but that is superfluous. It is an error to use "--" before the last word in a .vcp file. LIMITATIONS There is no way (yet) of telling the stringeditor to continue processing the rules list. We could implement labels like " <<*label*"> > to be allowed before pattern expressions (but not between pattern and result), and we could then impelement " < >. And a " < > could be used to fall through to the next label. All of which is wonderful, but I want to gain some real world experience with the current system and find a use case for gotos and fallthroughs before I implement them. This comment is here to solicit feedback :). AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::stringedit usage' => <<'TOPIC', Usage: StringEdit: ## Convert illegal p4 characters to ^NN hex escapes and the ## p4 wildcard "..." to a safe string. The "^" is not an illegal ## char, it's replaced with an escape to allow us to use it as ## an escape character without the (extremely small) risk of ## running across a file name that actually uses it. ## Order is significant in this ruleset. # field(s) match replacement name,labels /([\s@#*%^])/ ^%02x name,labels "..." ^___ StringEdit: ## underscorify each unwanted character to a single "_" name,labels /[\s@#*%^]/ _ StringEdit: ## underscorify each run of unwanted characters to a single "_" name,labels /[\s@#*%^]*/ _ StringEdit: ## prefix labels that don't start with a letter or underscore: labels /([^a-zA-Z_])/ _%c TOPIC ######################################################################## 'filter::stringedit description' => <<'TOPIC', Allows field by field string editing, using Perl regular expressions to match characters and substrings and sprintf-like replacement strings. Rules ===== A rule is a triplet of expressions specifying a (1) set of fields to match, (2) a pattern to match against those fields' contents (matching contents are removed), and (3) a string to replace each of the removed bits with. NOTE 1: the "match" expression uses perl5 regular expressions, not filename wildcards used in most other places in VCP configurations. The list of rules is evaluated top down and all rules are applied to each string. NOTE 2: The all-rules-apply nature of this filter is different from the behaviors of the ...Map: filters, which stop after the first matching rule. This is because ...Map: filters are rewriting entire strings and there can be only one result string, while the StringEdit filter may be rewriting pieces of string and multiple rewrites may be combined to good effect. The Fields List =============== A comma separated list of field names. Any field may be edited except those that begin with "source_". The Match Expression ==================== For each field, the match expression is run against the field and, if it matches, causes all matching portions of string to be replaced. The match expression is a full perl5 regular expression enclosed in /.../ delimiters or a plain string, either of which may be enclosed in '' or "" delimiters if inline spaces are needed (rare, we hope). The Replacement Expression ========================== Each match is replaced by one instance of the replacement expression, optionally enclosed in single or double quotation marks. The replacement expression provides a limited list of C sprintf style macros: %d The decimal codes for each character in the match %o The octal codes for each character in the match %x The hex codes for each character in the match Any non-letter preceded by a backslash "\" character is replaced by itself. Some more or less useful examples: \% \\ \" \' \` \{ \} \$ \* \+ \? \1 If a punctuation character other than a period (.) or slash "/" follows a letter macro, it must be escaped using the backslash character (this is to reserve room in the spec for postfix modifiers like "*", "+", and "?"). So, to put a literal star (*) after a hex code, you would do something like "%02x\*". the_future %x* %x{1} %x{1,} %x{,3} %x{1,3} ========================================== The "normal" perl5 letter abbreviations are also allowed: \t tab (HT, TAB) \n newline (NL) \r return (CR) \f form feed (FF) \b backspace (BS) \a alarm (bell) (BEL) \e escape (ESC) \033 octal char (ESC) \x1b hex char (ESC) \x{263a} wide hex char (SMILEY) \c[ control char (ESC) \N{name} named Unicode character including the following escape sequences are available in constructs that modify what follows: \l lowercase next char \u uppercase next char \L lowercase till \E \U uppercase till \E \E end case modification \Q quote non-word characters till \E As shown above, normal sprintf-style options may be included (and are recommended), so %02x produces results like "%09" (if the match was a single TAB character) or "%20" (if the match was a SPACE character). The dot precision modifiers (".3") are not supported, just the leading 0 and the field width specifier. Case sensitivity ================ By default, all patterns are case sensitive. There is no way to override this at present; one will be added. Command Line Parsing ==================== For large stringedits or repeated use, the stringedit is best specified in a .vcp file. For quick one-offs or scripted situations, however, the stringedit: scheme may be used on the command line. In this case, each parameter is a "word" and every triple of words is a ( pattern, result ) pair. Because vcp command line parsing is performed incrementally and the next filter or destination specifications can look exactly like a pattern or result, the special token "--" is used to terminate the list of patterns if StringEdit: is used on the command line. This may also be the last word in the "StringEdit:" section of a .vcp file, but that is superfluous. It is an error to use "--" before the last word in a .vcp file. test_script t/61stringedit.t ============================ TOPIC ######################################################################## 'filter::labelmap' => <<'TOPIC', NAME VCP::Filter::labelmap - Alter or remove labels from each revision SYNOPSIS ## From the command line: vcp labelmap: "rev_$rev_id" "change_$change_id" -- ## In a .vcp file: LabelMap: foo-... <> # remove all labels beginning with foo- F...R <> # remove all labels F v-(...) V-$1 # use uppercase v prefixes DESCRIPTION Allows labels to be altered or removed using a syntax similar to VCP::Filter::map. This is being written for development use so more documentation is needed. See VCP::Filter::map for more examples of pattern matching (though VCP::Filter::labelmap does not use syntax). AUTHOR Barrie Slaymaker COPYRIGHT Copyright (c) 2000, 2001, 2002 Perforce Software, Inc. All rights reserved. See VCP::License ("vcp help license") for the terms of use. TOPIC ######################################################################## 'filter::labelmap usage' => <<'TOPIC', Usage: ## From the command line: vcp labelmap: "rev_$rev_id" "change_$change_id" -- ## In a .vcp file: LabelMap: foo-... <> # remove all labels beginning with foo- F...R <> # remove all labels F v-(...) V-$1 # use uppercase v prefixes TOPIC ######################################################################## 'filter::labelmap description' => <<'TOPIC', Allows labels to be altered or removed using a syntax similar to VCP::Filter::map. This is being written for development use so more documentation is needed. See VCP::Filter::map for more examples of pattern matching (though VCP::Filter::labelmap does not use syntax). test_script t/61labelmap.t ========================== TOPIC