The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Examples

USING THE BRANCHOFF:ROOT MODEL

The Config Spec

##:BranchOff: root
element * CHECKEDOUT
element * .../tt/LATEST
mkbranch tt
element * TTT
element * BL
element /main/0

Most of this is standard. Let's read it from the bottom up:

/main/0: handle new elements, not bearing any label yet
BL: a common baseline
TTT: our own label--more on it later
mkbranch tt: a directive applying to the 3 previous (which follow it)
.../tt/LATEST: catch our versions, before we labeled them
CHECKEDOUT: catch our versions, before we checked them in
##:BranchOff: root: an extension of this wrapper

What these rules describe is a process by which the user:

  • Always and exclusively edits files in her own branches

  • Applies labels as she pleases, either to communicate of her progress to others while offering consistent change sets, or just to keep save points for herself

  • Delivers by moving a baseline label which she uses herself (we'll see later how she ensures that she doesn't let her previous deliveries hide common changes)

  • May at any time merge home, i.e. rebase the versions she is actively working on in her branches.

One of our goals will be to make this config spec as stable as possible, i.e. to manage changes by means not requiring us to modify it. A good reason for that is that config specs are not versioned.

A second goal is to systematically branch out for making new versions (unless already on a tt branch). The rationale is that this is always possible, independent from mastership or checked out state.

A third goal is to avoid cascading, thus to always branch from the root branch (usually main). This must be explicitly enabled, and is thus governed by our special setting on line 1.

First Use: Nothing Special

$ ct mkbrtype -nc tt
Created branch type "tt".
$ ct co -nc .
Created branch "tt" from "." version "/main/0".
Checked out "." from version "/main/tt/0".
$ ct mkdir -nc a
Created directory element "a".
Created branch "tt" from "a" version "/main/0".
Checked out "a" from version "/main/tt/0".
$ ct mkelem -nc -ci a/foo
Created element "a/foo" (type "text_file").
Created branch "tt" from "a/foo" version "/main/0".
Checked in "a/foo" version "/main/tt/1".
$ ct ci -nc a
Checked in "a" version "/main/tt/1".

APPLYING INCREMENTAL LABELS

Create a family label type

$ ct mklbtype -c 'Family demo' -fam TTT
Created label type "TTT_1.00".
Created label type "TTT".
Created attribute type "RmTTT".

Note that this creates:

A floating type: TTT
An equivalent fixed type: TTT_1.00
An attribute type dedicated to the family (see: rmlabel below).

Apply the base labels

$ ct mklabel -rec TTT .
Created label "TTT_1.00" on "." version "/main/tt/1".
Created label "TTT" on "." version "/main/tt/1".
Created label "TTT_1.00" on "a" version "/main/tt/1".
Created label "TTT" on "a" version "/main/tt/1".
Created label "TTT_1.00" on "a/foo" version "/main/tt/1".
Created label "TTT" on "a/foo" version "/main/tt/1".

The labels are applied in pairs: fixed, and floating. The first application is thus significantly slower than usual (~4 times), but this impact is typically negligible, on an initial tree with only few elements.

Lock the label types

$ ct lock lbtype:TTT
Locked label type "TTT".
Locked label type "TTT_1.00".

This is not mandatory of course, but it is a good idea, and it is supported. The fixed type should not be moved anymore, so why not locking it?

The floating label will get unlocked as needed, but locking it communicates its state to others, and leaves a timestamp (the latest one does not get scrubbed).

Increment the label type

$ ct mklbtype -c 'Increment demo' -inc TTT
Unlocked label type "TTT".
Unlocked label type "TTT_1.00".
Created label type "TTT_1.01".
Locked label type "TTT_1.00".

A new incremental fixed label is created. It gets linked to the previous one, which is thus unlocked, and locked back.

Make a change, and label incrementally

$ ct co -nc a/foo
Checked out "a/foo" from version "/main/tt/1".
$ ct ci -nc -ide a/foo
Checked in "a/foo" version "/main/tt/2".
$ ct mklabel -over tt TTT .
Created label "TTT_1.01" on "a/foo" version "/main/tt/2".
Moved label "TTT" on "a/foo" from version "/main/tt/1" to "/main/tt/2".

We use the -over tt option as a way to optimize the labeling speed. Note that it also restricts the application to checked-in versions.

Labeling recursively would display the same incremental behaviour, possibly applying labels to checked-out versions.

Check the results so far

$ ct lsgen a/foo a
a/foo@@/main/tt/2 (TTT, TTT_1.01)
 a/foo@@/main/tt/1 (TTT_1.00)
a@@/main/tt/1 (TTT, TTT_1.00)

We examine two objects, with the lsgenealogy command.

This one navigates Merge hyperlinks to present the recent history of contribution to the selected versions.

We note that the floating label designates a (full) baseline, whereas the fixed labels are sparse and designate thus the successive change sets.

Increment again, to remove a label

$ ct mklbtype -c 'Remove label' -inc TTT
Created label type "TTT_1.02".
$ ct rmlabel TTT a/foo
Removed label "TTT" from "a/foo" version "/main/tt/2".

What we want to demonstrate now is a way to restore a past configuration. For this we shall still create one version and apply one more increment, in order to build up enough of history.

$ ct co -nc a/foo
Checked out "a/foo" from version "/main/tt/2".
$ ct ci -nc -ide a/foo
Checked in "a/foo" version "/main/tt/3".
$ ct mklbtype -c 'Restore old config' -inc TTT
Created label type "TTT_1.03".
$ ct mklabel -over tt TTT .
Created label "TTT_1.03" on "a/foo" version "/main/tt/3".
Created label "TTT" on "./a/foo" version "/main/tt/3".

Note that the floating label was created, not moved, since we had removed it previously.

ARCHIVING BRANCHES AND LABELS

Archive to label

Currently, our config spec selects the version of foo with our tt branch, i.e. doesn't use our labels. Let's change this:

$ ct ls a/foo
a/foo@@/main/tt/3                                        Rule: .../tt/LATEST
$ ct mkbrtype -nc -arc tt
Renamed branch type from "tt" to "tt-001".
Created branch type "tt".
$ ct ls a/foo
a/foo@@/main/tt-001/3                                    Rule: TTT [-mkbranch tt]

Now we are using our label, placing ourselves in the same situation as anybody else who would want to share our code in a collaborative manner.

This step has admittedly only a temporary effect, and thus doesn't change much: it is only a publication but not yet a delivery. It may help us to narrow down the change set on which we are actively working without losing sight that we did not yet deliver our prior results.

Note how we didn't need to change our config spec, but only to reinterpret it.

Note finally how our config spec preserved the continuity of the selection.

Checking out after archiving

$ ct co -nc a/foo
Created branch "tt" from "a/foo" version "/main/0".
Checked out "a/foo" from version "/main/tt/0".
$ ct ci -c 'after archiving' -ide a/foo
Checked in "a/foo" version "/main/tt/1".
$ ct lsgen a/foo
a/foo@@/main/tt/1
 a/foo@@/main/tt-001/3 (TTT, TTT_1.03)
  a/foo@@/main/tt-001/2 (TTT_1.01)
   a/foo@@/main/tt-001/1 (TTT_1.00)

Now, we really experimented our cascade prevention, i.e. branching off root. The continuity of the data is preserved, as well, using the lsgenealogy tool, as this of the version history.

Delivery

We could deliver directly from branches, assuming all the changes would still be in branches of the same type. If this is not the case, we'd rather consolidate our results first, using labels. We may start with a home merge (aka rebase):

$ ct findmerge . -fve BL -merge

Assuming this confirmed that no delivery had taken place which we wouldn't have already taken into consideration, we can now label and archive the branches, skipping for now the outputs of the commands:

$ ct mklbtype -c Consolidation -inc TTT
$ ct mklabel -over tt TTT
$ ct mkbrtype -nc -arc tt

Now the delivery, from the TTT labels:

$ ct mklbtype -nc -inc BL
$ ct mklabel -over TTT BL
$ ct mklbtype -nc -arc TTT
Renamed label type from "TTT" to "TTT-001".
Created label type "TTT".
Renamed label type from "TTT" to "TTT_0".
Locked label type "TTT_0".
Locked label type "TTT-001".
Locked label type "TTT_1.00".
$ ct lock lbtype:BL

Two interesting points here (beyond the process itself):

  • The fact that we could unlock, and lock back the BL type, although we do not own it: this is an extension of the wrapper (the events are duly logged) to allow group members (configurable) to lock/unlock each other's types

  • The way archiving label types differed from archiving branch types:

    • As for branch types, the label type is renamed away from matching anything via the config spec

    • Again similarily, a new type is created but contrarily to the case of branch types, this one is not left in the open, but hidden away.

The new hidden type keeps track of the state of the family (what would the next increment be), and it would be resurrected by a next mklbtype -fam command.

One only creates a label type when one intends to use it explicitly, whereas a brtype is there just in case, to be used implicitly in a checkout.

Anyway, the result of the delivery is that we are using everything by the BL labels.

REPRODUCING A PAST CONFIGURATION

Clone a view, in the state it was on January 1.

$ ct mkview -tag ttt_Jan1 -clone ttt -equiv TTT,1-Jan
Created view.
Host-local path: jeeves:/views/mg/ttt.vws
Global path:     /views/mg/ttt.vws
It has the following rights:
User : mg       : rwx
Group: foo      : rwx
Other:          : r-x
$ diff /views/mg/ttt.vws/config_spec /views/mg/ttt_Jan1.vws/config_spec
0a1
> time 2011-01-01T00:00:00+0000
3d3
< element * .../mg/LATEST
5c5
< element * TTT
---
> include /views/mg/ttt_Jan1.vws/TTT.2011-01-01T00:00:00+0000
$ ct setview ttt_Jan1
$ ct ls -d a/foo a
a/foo@@/main/tt-001/2            Rule: {lbtype(TTT_1.01)&&!attr_sub(RmTTT,<=,1.01)}
a@@/main/tt-001/1                Rule: {lbtype(TTT_1.00)&&!attr_sub(RmTTT,<=,1.01)}

The script made three modifications to the initial config spec:

  • It added a time rule, corresponding to the specified time..

  • It removed rules based on branches using a type created after the specified time.

  • It replaced the TTT rule (hence preserving the order of the rules) with a directive including set of rules, equivalent to the TTT floating label at the specified time. This is achieved by determining the the fixed label in the family, which was then 'equivalent' to TTT: TTT_1.01.

We checked that these rules selected the correct versions of our two reference elements (the foo file, and the a directory).

Let's examine the new file:

$ cat /views/mg/ttt.vws/TTT.2011-01-01T00:00:00+0000
element * "{lbtype(TTT_1.01)&&!attr_sub(RmTTT,<=,1.01)}"
element * "{lbtype(TTT_1.00)&&!attr_sub(RmTTT,<=,1.01)}"

Examine config specs equivalent to 1.02 and 1.03 stages of TTT

$ ct mkview -tag ttt_1.02 -clone ttt -equiv TTT_1.02
$ ct setview ttt_1.02
$ ct ls -d a/foo a
a/foo@@/main/0                   Rule: /main/0 [-mkbranch tt]
a@@/main/tt-001/1                Rule: {lbtype(TTT_1.00)&&!attr_sub(RmTTT,<=,1.02)}
$ ct mkview -tag ttt_1.03 -clone ttt -equiv TTT_1.03
$ ct setview ttt_1.03
$ ct ls -d a/foo a
a/foo@@/main/tt-001/3            Rule: {lbtype(TTT_1.03)&&!attr_sub(RmTTT,<=,1.03)}
a@@/main/tt-001/1                Rule: {lbtype(TTT_1.00)&&!attr_sub(RmTTT,<=,1.03)}

We can now see the meaning of RmTTT, and see how attributes of this type were added to the different versions of the tree:

$ ct lsvtree -s a/foo | perl -ple 's/(.*)/des -fmt "\%Vn \%Nl \%Na\\n" $1/' | ct
/main
/main/0
/main/tt-001
/main/tt-001/1 TTT_1.00 RmTTT=1.02
/main/tt-001/2 TTT_1.01 RmTTT=1.02
/main/tt-001/3 TTT_1.03
/main/tt-002
/main/tt-002/1 BL BL_2.13 TTT-001 TTT_1.04

This command shows the versions, with the labels, and the attributes they bear. Of course, one can also use lsgen (not showing the attributes):

$ ct lsgen a/foo
a/foo@@/main/tt-002/1 (BL, BL_2.13, TTT-001, TTT_1.04)
 a/foo@@/main/tt-001/3 (TTT_1.03)
  a/foo@@/main/tt-001/2 (TTT_1.01)
   a/foo@@/main/tt-001/1 (TTT_1.00)

Given the profusion of labels, the lsgen command was made to support an enhanced version of the -fmt option, allowing to filter label types with a Perl regexp. Note that this also allows e.g. to show attributes, to shorten the output to versions only (the element name being common), and to display comments:

$ ct lsgen -fmt '%Vn %[^TTT(_[\d.]+)?$]Nl %Na "%Nc"' a/foo
/main/tt-002/1 TTT_1.04  "after archiving"
 /main/tt-001/3 TTT_1.03  ""
  /main/tt-001/2 TTT_1.01 RmTTT=1.02 ""
   /main/tt-001/1 TTT_1.00 RmTTT=1.02 ""

Pick the right increment/date

One needs a convenient way to figure what increment and what date correspond to which changes. Fortunately, the -family extension of describe allows you to apply the formats of your choice to each and every element of the type family:

$ ct des -fmt "%n %d %Nc\n" -fam lbtype:TTT-001
TTT_1.04 2011-01-31T16:30:41Z Consolidation
TTT_1.03 2011-01-28T19:51:17Z Restore old config
TTT_1.02 2011-01-28T19:49:35Z Remove label
TTT_1.01 2011-01-28T19:41:55Z Increment demo
TTT_1.00 2011-01-26T17:35:39Z Family demo