Fossil Wrapper

Fossil Wrapper


This project provides a script (fsl) written in Tcl/Expect with the following features:

Though still inchoate, you should be able to use it as you would fossil on the command line.

N.B. As a supplement to the documentation here, the Cookbook supplies useful examples of fsl enhancements. There is also a tutorial available that explains things in bite-sized chunks.



Since the script makes use of dictionaries and anonymous functions, it requires Tcl >= 8.5. Installation of expect should be straightforward on most systems:

Placing fsl on the $PATH

Simply copy the fsl script to a directory on $PATH:

$ echo $PATH
$ cp fsl ~/bin


Out of the box, a number of filters and aliases are defined in ~/.fslrc (created on first run); see here for more information. The configuration file is a Tcl script: you can define procs (for helper functions) and employ them in your filters (definitions will end up in the config::fslrc namespace so you needn't worry about clobbering predefined functions).

To view a summary of these definitions use the fsl wrapper pseudo-command:

$ fsl wrapper
=> Aliases:
   . d , log heads
=> Filters:
   changes status timeline add rm addremove leaves d branch
=> Interceptors:
   wr wra wrap wrapp wrappe wrapper he hel help ali alia alias aliase aliases

Additional information on expansions can be listed using fsl aliases:

$ fsl aliases
Currently defined expansions:
         . -> changes
         d -> diff
         , -> ui
       log -> timeline
     heads -> leaves

Defining aliases

Alias declarations are quite straightforward; their first argument is a command trigger, their second a bareword or quoted string, e.g.

alias log      timeline
alias history {timeline -n 100}

With these in place,

Abbreviations can be declared using span syntax. For example, to introduce a more flexible fsl heads alias,

alias hea:heads leaves

Now any of the following will trigger the expansion:

$ fsl hea
$ fsl head
$ fsl heads

Disjunctions may also be used as part of the command trigger, e.g.

alias heads|tips leaves

Since disjunction takes precedence over span specification, the above patterns can be combined into a single declaration:

alias hea:heads|tip:tips leaves

Writing filters

Filters are named, allowing them to be referenced elsewhere in your configuration script. Their structure is:

filter <internal name> <list of commands to filter> <body>

If you filter on a fossil command like diff, aliases of this command (by default, d) will also be filtered. Conversely, filtering on an alias (d), leaves its expansion (diff) untouched. These aliases may use span and disjunction syntax as described above. Also note that more than one filter may apply to a given command: under the default configuration, the timeline will be filtered by both status and log_entry.

A filter body can reference the current output line via the implicitly defined $line variable. By way of example, here's the log_entry filter:

filter log_entry {leaves timeline} {
    if {[regexp "^=== .* ===" $line]} {
        coloured blue $line
    } else {
        regsub -all {\[[A-Fa-f0-9]+\]} $line [coloured yellow &]

This filter is named log_entry (a proc in config::fslrc) and applies the defined substitutions to the output of leaves and timeline.

As listed, the log_entry filter will not be run on fsl time. Where a filter should apply to all abbreviations, simply use span syntax in the definition,

filter log_entry {leaves tim:timeline} {
    if {[regexp "^=== .* ===" $line]} {
        coloured blue $line
    } else {
        regsub -all {\[[A-Fa-f0-9]+\]} $line [coloured yellow &]

Command interception

Interceptors offer more fine grained control over command flow by providing a means to pre-process, override and define new commands. They come in two flavours:

Three interceptors are defined out of the box:

All of the above may be abbreviated.

Controlling interception

Interceptors can refer to the current parameter list via an implicit $params variable and are registered using a command trigger.

The distinction between pseudo-command and pre-processor boils down to what is returned:

Interceptors may also be used to call Fossil multiple times; this can be useful for scripting a repetitive workflow. To introduce additional calls, simply use the fossil command,

interceptor demo {
    fossil version
    fossil info -l
    return {}

For further examples, see the Cookbook.


Useful functions defined in fsl: