Fossil
Cookbook
Not logged in

Fossil Cookbook

The Fossil Cookbook is a collection of task-oriented instructions for intermediate users who wish to do more than basic operations with their Fossil repositories. The tutorial is a more suitable place for newcomers trying to learn the basic concepts to go first before tackling these recipes.

Index

Using Fossil's Built-In CGI

Motivation

Problem

Unlike some other distributed SCMs, Fossil can only clone, push, pull and and otherwise interact through repositories over HTTP. This can be simply and easily managed through use of the fossil server and/or fossil ui commands, of course, but this is really only adequate for ad-hoc repository sharing. Consider, for example, sharing ten repositories. Using the built-in server would require you to open ten ports in your firewall to permit access. Any serious sharing will require something more robust and permanent, and solutions for doing so are described below.

Solution

Fossil supports three different ways to share repositories. For ad-hoc sharing fossil server/ui is more than adequate. For more robust solutions, however, the use of (x)inetd or CGI support is indicated.

Setting up fossil for CGI support is simple. (Setting up your web server for CGI support may or may not be simple, but it is out of scope of this recipe. Consult your web server/service provider's documentation for this.)

UNIX

  1. Find your CGI scripts directory (if applicable). This is commonly something like <base>/cgi-bin/, but does not have to be.
  2. Inside that directory build a script file which looks like this:
      #! /usr/bin/env fossil
    repository: /full/path/to/repository/file.fsl
  3. Ensure that the script file so generated is set executable for the CGI user account.
  4. Ensure that every directory in the path leading to the repository is browseable (chmod +x) to the CGI user account.
  5. Ensure that the repository file is readable and writable to the CGI user account.

The following shell script can be run from within the directory containing the Fossil repositories to be shared (and, of course, altered for your setup) to set some of the constraints above up automatically:

 1 #! /bin/sh
 2 CGI_ROOT=/usr/lib/fossil
 3 REPOSITORY_OWNER=michael
 4 CGI_GROUP=www-data
 5 if [ "`id -un`" = 'root' ]
 6 then
 7   for repository in *.fsl
 8   do
 9     SOURCE=`pwd`/$repository
10     DESTINATION=$CGI_ROOT/${repository%.fsl}
11     echo "#! /usr/bin/env fossil" > $DESTINATION
12     echo "repository: $SOURCE" >> $DESTINATION
13     chown $REPOSITORY_OWNER:$CGI_GROUP $SOURCE
14     chmod 664 $SOURCE
15     chown root:root $DESTINATION
16     chmod 755 $DESTINATION
17   done
18 else
19   sudo $0 $*
20 fi

Here is a little perl script to put in your cgi-bin to list all the fossils you are publishing:

 1  #!/usr/bin/perl -w
 2  my $CGI_BIN = '/Library/WebServer/CGI-Executables';
 3  my @files = `grep -l repository:  $CGI_BIN/* `;
 4  print <<EOM;
 5  Content-Type: text/html
 6 
 7  Fossils for this server
 8  <ul>
 9  EOM
 10 
 11  for (@files) {
 12    s{.*/}{};
 13    next if /~$/;
 14    print "<li><a href='$_'>$_</a></li>\n";
 15  }
 16  print "</ul>\n";

The following apache2 configuration can be used to run the root of a web site with fossil, but still allow other services / documents to be reached via specific URLs. Replace "code.autonomo.us" with your site's name and "dclark@pobox.com" with your email.

NameVirtualHost *:80
<VirtualHost *:80>
    ServerName code.autonomo.us
    ServerAdmin dclark@pobox.com
    ErrorLog /var/log/apache2/code.autonomo.us-error.log
    LogLevel warn
    CustomLog /var/log/apache2/code.autonomo.us-access.log combined
    ServerSignature On

    DocumentRoot /var/www/

    ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
    <Directory "/usr/lib/cgi-bin">
        AllowOverride None
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
        Order allow,deny
        Allow from all
    </Directory>

    # Fossil SCM at root of web site (http://example.com) configuration...
    RewriteEngine On
    # RewriteCond - One for every URL we don't want Fossil SCM to serve. In the
    #               example, requests that go to the /var/www/tmp directory and 
    #               the /usr/lib/cgi-bin directoty are ignored by Fossil SCM.
    RewriteCond %{REQUEST_URI} !^/tmp/.*$
    RewriteCond %{REQUEST_URI} !^/cgi-bin/.*$
    RewriteRule ^(.*)$ /usr/lib/cgi-bin/code.autonomo.us/$1 [T=application/x-httpd-cgi]
</VirtualHost>

/usr/lib/cgi-bin/code.autonomo.us is just a standard fossil CGI file; it looks like this:

#!/usr/bin/fossil
repository: /srv/fossil-scm/code.autonomo.us.fossil-scm

Another solution to automatically serve multiple repositories

With the following CGI script (I have named it p simply) it is possible to define a location where all the your repositories should be located (in this example: /home/repos/fossil). All the repositories are named according the scheme <project name>.fsl for sake of this example.

If this script is called alone (like e.g.: http://your.server.here/cgi-bin/p), it will list all repositories located under $REPOSROOT, if there does not exist a corresponding file .<project name> (that mechanism is used to hide some repository in the listing).

If the script is called like e.g. http://your.server.here/cgi-bin/p/<project name>, then the corresponding repository will be selected to work with.

The lines 5 - 8 in the script below allow configuration for you needs. You have to chose, what fossil binary to use, what extension your repository databases have and where all your databases are located.

 1 #!/bin/sh
 2
 3 ### Configuration section ###
 4
 5 TITLE="<h2>Fossil repositories</h2>"
 6 FOSSIL=/usr/local/bin/fossil
 7 REPOSROOT=/home/repos/fossil
 8 REPOSEXT=".fsl"
 9
10 ### Processing section -- nothing to configure here! ###
11
12 set -f    ; # disable filename globbing
13
14 ## Some helper functions
15
16 HeaderText () {
17   if [ -z "$headerWritten" ]; then
18     echo "Content-type: text/plain; charset=iso-8859-1"
19     echo
20     headerWritten=1
21   fi
22 }
23
24 HeaderHTML () {
25   if [ -z "$headerWritten" ]; then
26     echo "Content-type: text/html; charset=iso-8859-1"
27     echo
28     headerWritten=1
29   fi
30 }
31
32 Error () {
33   HeaderHTML
34   echo "<p><font color="red">ERROR: $1</font></p>"
35   exit 1
36 }
37
38 P () {
39   HeaderText
40   for var in $*; do
41     eval echo $var = "\$$var"
42   done
43 }
44
45 ## Do the real work here ...
46
47 project=`echo $PATH_INFO | sed -e 's!/\([^/]*\).*!\1!'| tr "[A-Z]" "[a-z]"`
48 repos="$REPOSROOT/$project$REPOSEXT"
49
50 if [ -r "$repos" ]; then
51   PATH_INFO=`echo $REQUEST_URI | sed -e "s!^$SCRIPT_NAME/$project\\([^?]*\\).*!\\1!"`
52   : ${PATH_INFO:=/}
53   SCRIPT_NAME=$SCRIPT_NAME/$project
54   TEMPFILE=`mktemp /tmp/fossil-$project.XXXXXX` || Error "Couldn't create tempfile"
55   trap "rm -f $TEMPFILE" EXIT QUIT INT TERM
56   echo repository: $repos > $TEMPFILE
57   $FOSSIL cgi $TEMPFILE
58 elif [ -z "$project" ]; then
59   HeaderHTML
60   echo "$TITLE"
61   echo "<ul>"
62   find $REPOSROOT -name "*$REPOSEXT" | \
63   while read repos; do
64     project=`basename $repos $REPOSEXT`
65     if [ -f $repos -a ! -e $REPOSROOT/.$project ]; then
66       echo "<li><a href='${REQUEST_URI}/$project'>$project</a></li>"
67     fi
68   done
69   echo "</ul>"
70 else
71   Error "No such project: $project"
72 fi
73
74 exit 0

Windows

While it is far from a perfect set of instructions.. here are some quick notes that should help windows users along the way...

Discussion

Using the CGI server is the best solution combining an existing web infrastructure and the sharing of many Fossil repositories. Unlike the ad-hoc solution which requires, in effect, a separate port for each simultaneously-shared repository, and which requires several instances of fossil running -- one for each shared repository -- the CGI approach uses URLs to distinguish between repositories and only (briefly) runs a copy of fossil when the repository is actually accessed.

Sharing repositories with CGI is really only worth the effort if more than one repository is being shared, however. With only one being shared, fossil server is likely more than adequate or the use of (x)inetd may be indicated. If, however, there is already an existing web infrastructure in place, CGI still may be preferred if only for consistency and maintainability of the system as a whole.

Using Environment variables

Motivation

Solution

Environment variables are used to customize some programme behaviour. On Linux/Unix they can be set in you ~/.bashrc or even on command line. On Windows they can be set in the system settings.

Fossil checks the following environment variables:

VISUAL/EDITOR contain the editor of your choice that is used to write the commit file.

TMP_DIR specifies the directory for temporary files.

GATEWAY_INTERFACE ???

SQLITE_FORCE_PROXY_LOCKING ???

USER

USERNAME

Example CSS

Motivation

The default UI may not be stylish in everyones eyes. It is up to you to change it. See the CSS code below from this site Wandering Horse
/* General settings for the entire page */
body {
  margin: 0ex 1ex;
  padding: 0px;
  background-color: white;
  font-family: "sans serif";
}

/* Make the links in the footer less ugly... */
a { color: #000f6a; }
a:link { color: #000f6a; }
a:visited { color: #000f6a; }
a:hover { background-color: #e3e3e3; }


hr {
  height: 3px;
  border-top: none; /*1px dashed #005;*/
  border-bottom: 1px dashed #005;
  border-left: none;
  border-right: none;
}
/* The project logo in the upper left-hand corner of each page */
div.logo {
  display: table-cell;
  text-align: center;
  vertical-align: bottom;
  color: #000f6a;
}

/* The page title centered at the top of each page */
div.title {
  display: table-cell;
  font-size: 2em;
  font-weight: bold;
  text-align: center;
  color: #000f6a;
  vertical-align: bottom;
  width: 100%;
}

/* The login status message in the top right-hand corner */
div.status {
  display: table-cell;
  text-align: right;
  vertical-align: bottom;
  color: #000f6a;
  font-size: 0.8em;
}

/* The header across the top of the page */
div.header {
  display: table;
  width: 100%;
  text-align: center;
}

/* The main menu bar that appears at the top of the page beneath
** the header */
div.mainmenu {
  padding: 2px 5px 2px 5px;
  font-size: 0.9em;
  text-align: center;
  letter-spacing: 1px;
  background-color: #e3e3e3;
  color: #000f6a;
  border: 1px inset black;
}

/* The submenu bar that *sometimes* appears below the main menu */
div.submenu {
  padding: 2px 5px 2px 5px;
  font-size: 0.9em;
  text-align: center;
  background-color: #e3e3e3;
  color: #000f6a;
}
div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
  padding: 2px 10px 2px 10px;
  color: #000f6a;
  background-color: #e3e3e3;
  text-decoration: none;
}
div.mainmenu a:hover, div.submenu a:hover {
  color: #e3e3e3;
  background-color: #000f6a;
}

/* All page content from the bottom of the menu or submenu down to
** the footer */
div.content {
  padding: 0ex 1ex 0ex 2ex;
}

/* Some pages have section dividers */
div.section {
  margin-bottom: 0px;
  margin-top: 1em;
  padding: 1px 1px 1px 1px;
  font-size: 1.2em;
  font-weight: bold;
  background-color: #e3e3e3;
  color: #000f6a;
}

/* The "Date" that occurs on the left hand side of timelines */
div.divider {
  background-color: #e3e3e3;
  color: #000f6a;
  border: 1px #bbbbff solid;
  font-size: 1em; font-weight: normal;
  padding: .25em;
  margin: .2em 0 .2em 0;
  float: left;
  clear: left;
}

/* The footer at the very bottom of the page */
div.footer {
  font-size: 0.8em;
  padding: 2px 5px 2px 5px;
  text-align: center;
  letter-spacing: 1px;
  background-color: #e3e3e3;
  color: #000f6a;
  border: 1px inset black;
}

/* Make the links in the footer less ugly... */
div.footer a { color: #000f6a; }
div.footer a:link { color: #000f6a; }
div.footer a:visited { color: #000f6a; }
div.footer a:hover { background-color: #000f6a; color: #e3e3e3; }

/* verbatim blocks */
pre.verbatim {
   background-color: #f5f5f5;
   padding: 0.5em;
}

/* The label/value pairs on (for example) the vinfo page */
table.label-value th {
  vertical-align: top;
  text-align: right;
  padding: 0.2ex 2ex;
}

/* For marking important UI elements which shouldn't be
   lightly dismissed. I mainly use it to mark "not yet
   implemented" parts of a page. Whether or not to have
   a 'border' attribute set is arguable. */
.achtung {
  color: #ff0000;
  background: #ffff00;
  border: 1px solid #ff0000;
}

table.fossil_db_generic_query_view {
  border-spacing: 0px;
  border: 1px solid black;
}
table.fossil_db_generic_query_view td {
  padding: 2px 1em 2px 1em;
}
table.fossil_db_generic_query_view tr {
}
table.fossil_db_generic_query_view tr.even {
  background: #ffffff;
}
table.fossil_db_generic_query_view tr.odd {
  background: #e5e5e5;
}
table.fossil_db_generic_query_view tr.header {
  background: #558195;
  font-size: 1.5em;
  color: #ffffff;
}

Source highlighting

Motivation

Problem

The main purpose of Fossil is to do versioning for source code. Although it provides a standalone server and lets you navigate through the repository files additional features like source code highlighting from my perspective (I am not a developer of Fossil) are out of scope for an SCM. Just keep the Unix principle: small little programs that do their task and do it well.

However to have source code highlighted in the presented web pages would still be desirable.

Solution

There are two scenarios how to implement such a feature:

I estimate that the pipe solution needs some more work/ code changes. Thus I am solely looking at the Javascript solution.

The SyntaxHighlighter is a library of some Javascript files, a little Flash application and a CSS file. The Flash application is for copying to clipboard, print and view source. You have the option to include all the files into your repository or use the files hosted at Google. The latter may only be an option if you are connected to internet all the time.

For syntax highlighting to work the Header and Footer templates need to be modified and a little code change has to be applied to the Fossil sources. The examples below assume you have added the syntax highlighting library to your repository into a directory www/scripts.

Header

<html>
 <head>
 <title>$<project_name>: $<title></title>
 <link rel="alternate" type="application/rss+xml" title="RSS Feed"
       href="$baseurl/timeline.rss">
 <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
       media="screen">
 <link rel="stylesheet" href="$baseurl/doc/tip/www/SyntaxHighlighter.css" type="text/css"
       media="screen">
 </head>

. . .

Footer

</div>
<div class="footer">
Fossil version $manifest_version $manifest_date
</div>
<script language="javascript" src="$baseurl/doc/tip/www/scripts/shCore.js"></script>
<script language="javascript" src="$baseurl/doc/tip/www/scripts/shBrushCpp.js"></script>
<script language="javascript">
dp.SyntaxHighlighter.ClipboardSwf = '$baseurl/doc/tip/www/scripts/clipboard.swf';
dp.SyntaxHighlighter.HighlightAll('code');
</script>
</body></html>

Fossil/src/info.c function artifact_page

if( zMime==0 ){
    @ <pre name="code" class="c">
    @ %h(blob_str(&content))
    @ </pre>

Discussion

The Javascript solution requires a minimum to be fully supported by Fossil. Of course my litte change only applies for C/C++ files. But only little more work needs to be done to get the extension of the file a guess the file type.

The pipe solution would also be nice but would probably need some more work than this little patch.

How to prepare your Windows XP Fossil development Environment

MinGW+NSIS/WiX

Visual Studio Express 2008+WiX

Javascript HTML WYSIWYG editor control

Motivation

Problem

Fossil by itself doesn't support it.

Solution

There are pure javascript editor components that can be used for this task. The source for the editor component is added to the repository. The html header or footer is prepared to include a javascript file and/or a CSS.
These two tips are from the mailing list: Rene de Zwart 30. Oct. 2009

TinyMCE

Source: TinyMCE

Example
    mkdir tiny
    mkdir tiny/javascript
    fossil new tinymce.fsl
    fossil ui tinymce.fsl {configure the project)
    download tinymce
    unzip in tiny/javascript
    cd tiny
    fossil open ../tinymce.fsl
    fossil add javascript
    fossil commit -m "added timymce to the project"
    fossil ui
Select admin/headers add after the </link>
 <th1>
   if { "tktnew" eq $current_page 
   	|| "tktedit" eq $current_page 
   	|| "wikiedit" eq $current_page 
   	|| "wikiappend" eq $current_page } {
       html "<script type='text/javascript'\n"
       html "src='/doc/tip/javascript/tinymce/jscripts/tiny_mce/tiny_mce.js'>\n"
       html "</script>\n"
       html " <script type='text/javascript'>\n"
       puts "tinyMCE.init({ mode : 'specific_textareas' , editor_selector : 'wikiedit', theme: 'advanced',width : '90%' } );"
       html "</script>\n"
   }
</th1>
and save.

markitup!

Source: Markitup

Example
    mkdir markitup
    mkdir markitup/javascript
    fossil new markitup.fsl
    fossil ui markitup.fsl {configure the project)
    download markitup and jquery
    unzip in markitup/javascript, cd latest, mv * .., rmdir latest
    copy jquery-....js to javascript/jquery.js
    cd markitup
    fossil open ../markitup.fsl
    fossil add javascript
    fossil commit -m "added markitup an jquery to the project"
    fossil ui
select admin/headers add after the </link> put
    <link rel="stylesheet" type="text/css" href="/doc/tip/javascript/markitup/skins/markitup/style.css" />
    <link rel="stylesheet" type="text/css" href="/doc/tip/javascript/markitup/sets/default/style.css" />
    <script type="text/javascript" src="/doc/tip/javascript/jquery.js">
    </script>
    <script type="text/javascript" src="/doc/tip/javascript/markitup/jquery.markitup.js">
    </script>

and save select admin/footer add above the first line

    <script type='text/javascript'>
      var m = document.getElementsByTagName('textarea')
      var l = m.length
      var n
      var mySettings = {
	nameSpace:       "html", // Useful to prevent multi-instances CSS conflict
	onShiftEnter:    {keepDefault:false, replaceWith:'<br />\n'},
	onCtrlEnter:     {keepDefault:false, openWith:'\n<p>', closeWith:'</p>\n'},
	onTab:           {keepDefault:false, openWith:'     '},
	markupSet:  [
	    {name:'Heading 1', key:'1', openWith:'<h1(!( class="[![Class]!]")!)>', closeWith:'</h1>', placeHolder:'Your title here...' },
	    {name:'Heading 2', key:'2', openWith:'<h2(!( class="[![Class]!]")!)>', closeWith:'</h2>', placeHolder:'Your title here...' },
	    {name:'Heading 3', key:'3', openWith:'<h3(!( class="[![Class]!]")!)>', closeWith:'</h3>', placeHolder:'Your title here...' },
	    {name:'Heading 4', key:'4', openWith:'<h4(!( class="[![Class]!]")!)>', closeWith:'</h4>', placeHolder:'Your title here...' },
	    {name:'Heading 5', key:'5', openWith:'<h5(!( class="[![Class]!]")!)>', closeWith:'</h5>', placeHolder:'Your title here...' },
	    {name:'Heading 6', key:'6', openWith:'<h6(!( class="[![Class]!]")!)>', closeWith:'</h6>', placeHolder:'Your title here...' },
	    {name:'Paragraph', openWith:'<p(!( class="[![Class]!]")!)>', closeWith:'</p>'  },
	    {separator:'---------------' },
	    {name:'Bold', key:'B', openWith:'<strong>', closeWith:'</strong>' },
	    {name:'Italic', key:'I', openWith:'<em>', closeWith:'</em>'  },
	    {name:'Stroke through', key:'S', openWith:'<del>', closeWith:'</del>' },
	    {separator:'---------------' },
	    {name:'Ul', openWith:'<ul>\n', closeWith:'</ul>\n' },
	    {name:'Ol', openWith:'<ol>\n', closeWith:'</ol>\n' },
	    {name:'Li', openWith:'<li>', closeWith:'</li>' },
	    {separator:'---------------' },
	    {name:'Picture', key:'P', replaceWith:'<img src="[![Source:!:http://]!]" alt="[![Alternative text]!]" />' },
	    {name:'Link', key:'L', openWith:'<a href="[![Link:!:http://]!]"(!( title="[![Title]!]")!)>', closeWith:'</a>', placeHolder:'Your text to link...' },
	    {separator:'---------------' },
	    {name:'Clean', replaceWith:function(h) { return h.selection.replace(/<(.*?)>/g, "") } },
	    {name:'Preview', call:'preview', className:'preview' }
	]
      }
      for(var i=0 ;i < l;i++){
	n = m[i].name
	if( 'comment' == n || 'cmappnd' == n || "w"  == n){
	    m[i].id = n
	    $(function() {
	      $("#" + n).markItUp(mySettings);
	    });
	}
      }
    </script>
 

Linking Tickets to Checkin's

It's easy to link a checkin to a ticket... simply include [ticket-uuid] in your commit comment. Now, when you do that, you also have the ability to link a ticket to the associated checkin(s).

This is just one way of doing this, and what I decided to do. You can of course alter it for your own style/layout.

First: Add a "Short UUID" to your View Ticket page. You can do this by:

<th1>
set shortUuid [string range $tkt_uuid 0 9]
</th1>

Then, display that to the user somewhere:

Uuid: $<shortUuid>

All that does is make it easy for the developer to copy/paste a sensible UUID into their commit log.

Now, continuing, on the same View Page add a link to view associated tickets:

<a href="$<baseurl>/timeline?y=ci&s=$<shortUuid>">associated tickets</a>

What this does is searches all checkin's for the tickets uuid, thus, you can now see all checkin's that are linked with the given ticket.

Fossil Th1

Purpose of Th1

Th1 is used as a template system for generating HTML header and footer. It is a TCL like language. If you know TCL you know TH1.

It is invoked by opening a <th1> tag. The first time it starts an interpreter. The state of this interpreter is valid during the page generation.

For example when you

  • do in the header <th1> set version beta </th1>
  • then in the footer <th1>puts "Version set in header is $version"</th1> results in 'Version set in header is beta'

Because it is used as a template system it exports some details of fossil through

  1. variables
  2. functions

These are defined in [590e073746121befe65565ee6d73007c37ade12c|src/th_main.c])

Variables

These variable are global and available outside <th1> tags. They can be referenced either as

  • $var. e.g. $title.
  • $<var> e.g. $<title>

The following are defined:

  • $base_url. The first part of the url. For example http://localhost:8080
  • $current_page. The page requested. For example editing a wikipage is done by $baseurl/wikiedit $current_page is wikiedit
  • $index_page. Mostly the value is /home setable in Admin/Configuration
  • $title. The title of the $current_page
  • $login. The name of user if a login has occurred. if you use this variable always check if it exists via info exists login
  • $manifest_version. The fossil version
  • $manifest_date. The date that fossil version was compiled

Functions

These are only avaible between <th1> and </th1>

  • combobox name text-list numlines. Generates a select box with name="name" and a cgi-parameter of "name"
  • enable_output boolean
  • linecount string max min. Counts the numer of newlines in string but not more then max and not less then min
  • hascap string. return true if user has all capabilities in string
  • htmlize string. excapes all chars in string which have special meaning in HTML
  • date returns the sqlite value of datetime('now')
  • html string output html
  • puts string prints the string
  • wiki string. The string is in wiki format. translate it to HTML

Example from admin/header

<html>
<head>
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$baseurl/timeline.rss">
<link rel="stylesheet" href="$baseurl/style.css" type="text/css"
      media="screen">
</head>
<body>
<div class="header">
  <div class="logo">
    <img src="$baseurl/logo" alt="logo">
    <br><nobr>$<project_name></nobr>
  </div>
  <div class="title">$<title></div>
  <div class="status"><nobr><th1>
     if {[info exists login]} {
       puts "Logged in as $login"
     } else {
       puts "Not logged in"
     }
  </th1></nobr></div>
</div>
<div class="mainmenu"><th1>
html "<a href='$baseurl$index_page'>Home</a> "
if {[hascap h]]} {
  html "<a href='$baseurl/dir'>Files</a> "
}

Caveats

  • puts "[hello hello]" doesn't work because [ triggers the interpeter, use puts "&#91hello hello]'

Versioning compressed files

Goal

Some file formats are actually zip archives containing text files. For example Microsoft Office 2007 and newer use docx, xlsx and pptx extensions to store what is merely xml files zipped into a file.

When edits are made to such files a versioning system is not efficient because diffing is no more appropriate

The idea is to version the decompressed folder. A script will toggle Compressed/Uncompressed state

Sample batch script for use with docx, xlsx and pptx documents

This script is intended to work under MS Windows. It requires zip.exe and unzip.exe that you can find for example in gnuwin32 (http://getgnuwin32.sf.net).

You have three variables to set. Running this script will

In this way you can work on a docx document, run the script, insert the uncompressed folder into fossil and run the script again to get your document back in editable state.

Important: Use the --dotfiles option to the add command to include rels/.rels file.

 1 :: toggle docx state (compressed / uncompressed)
 2 :: @echo off
 3
 4 SET DOCX=My_document.docx
 5 SET TMPFILE=tmp_version_compressed_archive.zip
 6 SET CMDZIP="P:\bin\zip.exe"
 7 SET CMDUNZIP="P:\bin\unzip.exe"
 8
 9 :: check whether it is in a directory or file state
10 if exist %TEMP%\%TMPFILE% del /q "%TEMP%\%TMPFILE%"
11 if exist %DOCX%\NUL (
12     call :zip_directory
13     goto :end
14 )
15 if exist %DOCX% (
16     call :unzip_file
17     goto :end
18 )
19 echo %DOCX% not found
20 goto :end
21
22 :zip_directory
23   echo Zipping %DOCX%
24   pushd %DOCX%
25   ren Content_Types.xml [Content_Types].xml
26   %CMDZIP% -9 -r "%TEMP%\%TMPFILE%" .\*
27   if not exist "%TEMP%\%TMPFILE%" (
28     :: cancel
29     ren [Content_Types].xml Content_Types.xml
30     popd
31     goto :end
32   )
33   popd
34   rmdir "%DOCX%" /s /q
35   move "%TEMP%\%TMPFILE%" .
36   ren "%TMPFILE%" "%DOCX%"
37   goto :end
38
39 :unzip_file
40   ren "%DOCX%" "%TMPFILE%"
41   move "%TMPFILE%" "%TEMP%"\
42   mkdir "%DOCX%"
43   pushd "%DOCX%"
44   %CMDUNZIP% "%TEMP%\%TMPFILE%"
45   if not exist [Content_Types].xml (
46     :: cancel
47     popd
48     rmdir "%DOCX%" /s /q
49     move "%TEMP%\%TMPFILE%" .
50     ren "%TMPFILE%" "%DOCX%"
51     goto :end
52   )
53   ren [Content_Types].xml Content_Types.xml
54   del "%TEMP%\%TMPFILE%"
55   popd
56   goto :end
57
58 :end
59
60 :: vim: fenc=cp437

Color selector in check-in properties

If you want a nice JS color selector in your check-in properties, put the following into your footer:
<th1>
  if { "$current_page" eq "ci_edit" } {
    html "<script src=\"http://jscolor.com/jscolor/jscolor.js\" type=\"text/javascript\"></script>"
    html "<script type=\"text/javascript\">"
    html "var myPicker = new jscolor.color(document.getElementById('clrcust'), {hash:true})"
    html "</script>"
  }
</th1>

This will only work with a version after 2010 Sep 29, 10:30