<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="description" content="Manual for p4ftpsync, a Python script to synchronize a remote site with a P4 depot via FTP." />
<meta name="keywords" content="Perforce,P4,FTP,sync,synchronization,remote,site,web,python" />
<meta name="author" content="Richard Gruet" />
<title>p4ftpsync Manual</title>
<style type="text/css">
h1,h2,h3,h4,h5 {
font-family:Arial, Helvetica, sans-serif;
margin-left: 5px;
}
h1 {
margin: 25px 10% 40px 10%;
margin-bottom: 40px;
border: gray dotted 2px;
background-color:#FFCC99;
}
h2 {
border-bottom: black solid 1px;
}
h4 {
margin-bottom: 5px;
}
div {
margin-bottom: 10px;
margin-left: 15px;
font-family: Georgia, "Times New Roman", Times, serif;
/* font-family: Geneva, Arial, Helvetica, sans-serif; */
font-size:12px;
}
div.optionCategory {
margin-top: 20px;
margin-right: 5%;
font-weight: bold;
font-style: italic;
border-bottom: gray solid 1px;
}
div.option {
font-weight: bold;
margin-left: 40px;
margin-bottom: 2px;
}
div.optionDesc {
margin-left: 80px;
}
pre {
border: black solid 1px;
border-width: 1px 0 1px 0;
margin: 7px 5% 7px 3%;
padding: 5px 5px 5px 5px;
background-color: #EFEFEF /* #FFCC99;*/
}
</style>
</head>
<body>
<div style="font-size:smaller">
<a href="javascript:location='mailto:\u0070\u0034\u0066\u0074\u0070\u0073\u0079\u006e\u0063\u0040\u0072\u0067\u0072\u0075\u0065\u0074\u002e\u006e\u0065\u0074';void 0">Richard Gruet</a> - Created September 15, 2005 - Last revised
<!-- #BeginDate format:Am1 -->September 20, 2005<!-- #EndDate --></div>
<h1 align="center">p4ftpsync <span style="font-size:smaller">- Synchronizes a remote site with a P4 depot via FTP</span></h1>
<h2>Table Of Contents</h2>
<div class="toc">
<ul>
<li><a href="#overview">Overview</a></li>
<li><ul>
<li><a href="#Whatsthat">What's that ?</a></li>
<li><a href="#WhatsFor">What's for ?</a></li>
</ul></li>
<li><a href="#requirements">Requirements</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#startup">Start-up</a></li>
<li><a href="#commandReference">Command Reference</a></li>
<li><ul>
<li><a href="#reverseSync">Reverse Synchronization</a></li>
<li><a href="#validOptions">Valid Options</a></li>
<li><a href="#defaultValues">Default Values</a></li>
<li><a href="#cache">Cache</a></li>
<li><a href="#generatedScript">Generated FTP script</a></li>
<li><a href="#log">Log</a></li>
</ul></li>
<li><a href="#limitations">Limitation, Bugs, Todo</a></li>
<li><a href="#license">License</a></li>
</ul>
</div>
<!-- OVERVIEW -->
<a name="overview" id="overview"></a><h2>Overview</h2>
<a name="Whatsthat" id="Whatsthat"></a>
<h3>What's that ?</h3>
<div><code>p4ftpsync.py</code> is a Python script which synchronizes a remote site with a P4 repository via Ftp, and vice versa. It is specially useful when you have not enough control over the remote host to be able to install a Perforce client there, which would make synchronization easy (at least the P4 to remote site sync).</div>
<a name="WhatsFor" id="WhatsFor"></a><h3>What for ?</h3>
<div>I am a member of a team which designs web sites. We want to keep the pages and code for our sites under P4 control,
and <strong>synchronize</strong> the remote live sites from time to time to reflect the changes. Occasionally we
have also to modify the pages <strong>directly</strong> on the live sites (in emergency situations!), and some
of our clients can even edit the pages directly using Macromedia <a href="http://www.macromedia.com/software/contribute/"
title="Macromedia Contribute">Contribute</a>: In these cases we want to have the P4 repository <strong>synced back</strong>.</div>
<div>If you have enough control over the web host where your live site resides, i.e. if you can install a P4 client, the first sync
(P4 to the live site) is easily accomplished via a <code>P4 sync</code> command, but not the second since P4 doesn't detect
automatically changes in workspaces, you still have to manually open the files for add or delete.
And if you have not enough privileges to be able to install a P4 client, which is the case of a lot of small to medium
sites using cheap Web Hosting, you just can't use the P4 client install/P4 sync strategy [this strategy and others are developped
in an interesting article on the P4 web site: <a href="http://www.perforce.com/perforce/wcm.html"> Web Content management in
Perforce</a>].</div>
<div>I searched for alternate solutions but could not find any - I could have missed something though, please let me know! -
So I eventually wrote a program to perform the P4 to remote site synchronization, which I extended later to perform
the reverse sync as well.</div>
<div> The problem is to emulate a P4 remote client accessible only via FTP. The basic idea is to use a dedicated local
client/workspace that reflects (mirrors) the state of the remote site. If we P4 sync this workspace to a certain
revision, we get the exact list of changes that occurred, which we can then propagate (<em>push</em>) to the remote
site using FTP operations. Conversely, with the restriction that the remote site be synced to the head revision,
we can detect changes made directly to the site by comparing the remote site and the local workspace (synced to
the head revision beforehand).</div>
<div>Basically that's what <code>p4ftpsync</code> does. It's plain <a href="http://python.org/"
title="Python language official site" target="_blank">Python</a>, one single loooooong file (>3000 lines!),
uses no external library (not even the Perforce Python API) and requires only the basic P4 client install + the
Python language (not required with the (almost) standalone executable version). It works with Perforce
server version 2005.1 but should work with older versions as well (it doesn't use any fancy features but relies
on P4 output parsing so it could be sensitive to change in format). The code uses some tricks to optimize speed
(file caching, multi-threaded FTP operations) and so far it seems to be rather reliable, at least for common simple
operations (eg sync to head revision).</div>
<!-- REQUIREMENTS -->
<a name="requirements" id="requirements"></a><h2>Requirements</h2>
<ul>
<li>Python 2.2+ (not required if the Windows standalone executable version is used). </li>
<li>Basic (text mode) Perforce client installed locally. No Python API required.</li>
<li>Your remote site accessible via FTP. The more possible simultaneous connections the faster transfers will be. </li>
</ul>
<!-- INSTALLATION -->
<a name="installation" id="installation"></a><h2>Installation</h2>
<div>You have the choice between 2 distributions : </div>
<ul>
<li>A Python script <a href="p4ftpsync.py" title="Python script">p4ftpsync.py</a>
(you will need <a href="http://www.python.org/download/">Python</a> 2.2+ to execute it).</li>
<li>A Win32 standalone executable <a href="p4ftpsync.exe" title="Standalone executable">p4ftpsync.exe</a>.
Actually you may also need to download <a href="MSVCR71.dll" title="MicroSoft C Runtime library">MSVCR71.dll</a>
if not already present on your system (this is because Python 2.4 is compiled with MicroSoft VC7, which creates
executables that depend on that dll). Put it in the same directory as the .exe.</li>
</ul>
<div>In both cases you must have installed at least the <a href="http://perforce.com/perforce/loadprog.html"
title="Perforce downloads" target="_blank">Perforce client</a>, and added the P4 install directory to
your <code>PATH</code> environment variable before you can run <code>p4ftpsync</code>.
Check it by opening a terminal and type: </div>
<pre>p4</pre>
<div>You should get a P4 help message. If not, check your P4 install and <code>PATH</code> envt var.</div>
<div><code>p4ftpsync</code> also requires that you <strong>create a dedicated P4 <em>client</em></strong> (workspace).
This workspace will be used to <em>mirror</em> the remote site in order to perform the synchronization.
Use the Perforce command <code>p4 client</code> to create a client. Define the client <em>map spec</em>
so to include the part of the depot that you want to synchronize.</div>
<!-- START-UP -->
<a name="startup" id="startup"></a><h2>Start-up </h2>
<div>We assume that you have installed everything as described above and created a dedicated P4 client
(called here <em>syncClient</em>). The examples described use the python script <code>p4ftpsync.py</code>,
but apply to the executable version as well.</div>
<div>To get help, type :</div>
<pre>[python] p4ftpsync[.py] -h (or --help)
</pre>
<div>You can skip the "python" if <code>p4ftpsync.py</code> is in your <code>PATH</code> and files with extension
<code>.py</code> are associated to the Python interpreter (which is generally the case). On windows, you can even
omit the extension <code>.py</code>, provided you have added .py to the list of extensions in the <code>PATHEXT</code>
environment variable.</div>
<div>Let's try a normal (P4 to remote site) synchronization. Say you have your site on ftp server <em>ftpServerAddress</em>
accessible by user <em>userName</em>, password <em>passwd</em>, rooted at <em>/mySite/www/</em>. Say the corresponding
location in the P4 depot is at <em>//depot/mySite/www/...</em>. You want to synchronize the remote site with the
head revision of the depot. Type:
</div>
<pre>
p4ftpsync.py -v -t --p4Passwd <em>password</em> --p4Client <em>syncClient</em> --ftpHost <em>ftpServerAddress</em>
--ftpUser <em>userName</em> --ftpPasswd <em>passwd</em> --ftpRoot <em>/mySite/www/</em> <em>//depot/mySite/www/...</em>
</pre>
<div>
<ul>
<li>If you omit a mandatory option, p4ftpsync will ask you to enter it interactively. Some P4 options are
<em>guessed</em> from the environment, and p4ftpsync saves certain option values from one session to the
other and uses them as defaults (more details about this later).</li>
<li>Option <strong>-v</strong> (or --verbose) displays more detailed information messages on the screen (there
is also an -always verbose- log, see file <code><em>sameDirAsP4ftpsync</em>/p4ftpsync.log</code>.</li>
<li>Option <strong>-t</strong> (or --test) performs a <em>dry run</em>: synchronization actions are listed but not
actually executed. The 2 options can be grouped together as <code>-vt</code> since they have no parameters.</li>
</ul>
</div>
<div>If you perform this sync for the <strong>first time</strong>, <code>p4ftpsync</code> will try to upload
<strong>all</strong> the files in <em>//depot/mySite/www/...</em> to the remote site. Actually, the actions
are the same as a <code>p4 sync //depot/mySite/www/...</code> would do, except that they are converted to ftp
actions to update the remote site (viewed as a remote P4 workspace). Next runs will only transfer changes since
the last run, as <code>P4 sync</code> would do (however option <strong>-f</strong> allows to <strong>force</strong>
the complete refresh of the workspace and therefore will upload all the files again).
</div>
<div>As you can see, there are a <strong>lot of parameters</strong> to provide to <code>p4ftpsync</code> !
Fortunately the program tries to <strong>help you</strong> :
<ul>
<li>by defaulting missing P4 parameters p4Host, p4User, p4Passwd to their current values in the environment
(P4PORT, P4USER, P4PASSWORD - actually the program gets this info by parsing the output of a <code>p4 info</code> command).</li>
<li> The values of several options are also <strong>saved</strong> on disk during a session and used as defaults in
the next session (passwords are <strong>never</strong> stored).</li>
<li> Finally, the user is prompted to dynamically enter the missing <strong>required</strong> parameters. </li>
</ul>
In spite of these facilities, if you have to routinely perform a sync task, it is more convenient to define
a small <strong>script</strong> for each site to synchronize, which will call <code>pftpsync</code> with the
appropriate arguments. I find personally easier to define <strong>two</strong> scripts for the normal and reverse
sync respectively (since they have different options), that I call (on Windows) <code>sync<em>MySite</em>live.bat</code>
and <code><strong>r</strong>sync<em>MySite</em>live.bat</code>. Here is an example of such scripts :
</div>
<pre>
@echo off
REM <strong>sync<em>MySite</em>live.bat</strong>:
REM Synchronizes (depot head revision -> live site) the <em>MySite</em> live site via FTP.
REM
REM For reverse synchronization, see rsync<em>MySite</em>live.bat.
REM Uses P4 workspace ("client") <em>syncClient</em> as a mirror.
REM You can pass additional args like :
REM -v, --verbose to get a more detailed trace,
REM -f, --force to force P4 to resync to the given revision
REM ... and many more! use option -h for details.
REM
p4ftpsync.py %* --p4Passwd <em>password</em> --p4Client <em>syncClient</em> --ftpHost <em>ftpServerAddress</em>
--ftpUser <em>userName</em> --ftpPasswd <em>passwd</em> /mySite/www/ --exclude @<em>p4ftpSyncDir</em>\sync<em>MySite</em>live.excludes.txt
//depot/mySite/www/...
</pre>
<div>(Replace <code>p4ftpsync.py</code> with <code>p4ftpsync.exe</code> if needed)</div>
<pre>
@echo off
REM <strong>rsync<em>MySite</em>live.bat</strong>:
REM <strong>Reverse</strong> synchronization (live site -> P4) of live site <em>MySite</em> via FTP.
REM Creates a P4 changelist for the changes and submits it.
REM
REM For normal synchronization (p4 -> live site), see sync<em>MySite</em>live.bat.
REM Uses P4 workspace ("client") <em>syncClient</em> as a mirror.
REM You can pass additional args like :
REM -v, --verbose to get a more detailed trace,
REM -f, --force to disable the file desc cache and force re-reading of remote files
REM ... and many more! use option -h for details.
REM
p4ftpsync.py %* <strong>--reverse --submit</strong> --p4Passwd <em>password</em> --p4Client <em>syncClient</em> --ftpHost <em>ftpServerAddress</em>
--ftpUser <em>userName</em> --ftpPasswd <em>passwd</em> /mySite/www/ <strong>--comment "GG3:rsync: Integrated changes made on the
<em>mySite</em> live site." --mailto "john@doe.org,jane@jungle.com"</strong> --exclude @<em>p4ftpSyncDir</em>\sync<em>MySite</em>live.excludes.txt
<strong>--smtpServer smtp.myIsp.com</strong> //depot/mySite/www/...
</pre>
<div>(options specific to <strong>reverse</strong> sync are in bold).
<ul>
<li>option <strong>--reverse</strong> tells <code>p4ftpsync</code> to perform a <em>reverse</em> synchronisation, instead
of the default "normal" one.</li>
<li>option <strong>--submit</strong> tells <code>p4ftpsync</code> to automatically submit the P4 changelist created
for the changes detected (if any). By default the changelist is not submitted to let you have a look at the
changes and possibly revert some of them (useful during your first trials).</li>
<li>option <strong>--comment</strong> overrides the default auto-generated changelist comment with a customized one.</li>
<li>option <strong>--mailto</strong> tells <code>p4ftpsync</code> to send a report e-mail to each address listed.
The <strong>--smtpServer</strong> option specifies the address of the SMTP server to use (the default is
<em>localhost:25</em>. Use options --smtpUser and --smtpPasswd if the server requires authentication).
Default is to <strong>not</strong> send an email report.</li>
</ul>
</div>
<div>Both scripts share the same <strong>list of exclusions</strong> (which makes sense). This is why the list is
contained in a <strong>external file</strong> (<em>p4ftpSyncDir</em>\syncMySitelive.excludes.txt), rather than
directly passed as a command line argument (this is indicated by the use of <code><strong>@</strong></code>
in option --exclude). Files to exclude from the sync are specified as <em>patterns</em>, one per line.
Patterns are actually Python <em>regular expressions</em>, implicitely terminated by <code>$</code>
and preceded by <code>^.*</code>, which in practice means that patterns will be matched against the
<strong>end</strong> of the files to check. For examples of patterns, see option <strong>--exclude</strong>
in the <em><a href="#commandReference">Command Reference</a></em> section below.
</div>
<div>Once you have perfectly tuned the parameters in your 2 scripts, you may consider <strong>scheduling</strong> their
execution, for example on a daily basis, using Control Panel/Scheduled Tasks on Windows, cron on Unix, etc...<br />
Synchronizing and reverse synchronizing is like having two different p4 clients accessing and modifying the
same files. If a file can be modified <strong>directly</strong> on the live site, then a situation of
<strong>conflict</strong> (e.g. simultaneous edits of the same file) is possible, and must be <em>resolved</em>.
By scheduling the <strong>reverse</strong> sync <strong>first</strong>, then the "normal" sync, rather
than the contrary, one guarantees that any conflict will be detected by Perforce and scheduled for resolve.
This is not true if the scripts are scheduled the other way (normal, then reverse): in this case any change
on the live site will be silently overwritten by a change (done elsewhere) already submitted in P4.
</div>
<div>The strategy above is somewhat "blind" in that it synchronizes the live site at fixed intervals without any
consideration for actual changes made. You don't always want to propagate changes submitted in P4 to
the live site ASAP. Maybe you want to accumulate changes somewhere and once you are ready, transfer
them to the live site. A possible strategy to achieve this is to create a <strong>branch for the sync</strong>:
you make the changes into the main/development branch, and when you feel ready you <em>integrate</em>
them into the sync branch, which of course will be the one specified for the <em>whatToSync</em> parameter
to <code>p4ftpsync</code>.
</div>
<!-- COMMAND REFERENCE -->
<a name="commandReference" id="commandReference"></a>
<h2>Command Reference</h2>
<div style="font-size:smaller">(this is basically a formatted copy of the ouput of <code>p4ftpsync --help</code>)</div>
<pre>[python] p4ftpsync[.py] [options] <em>whatToSync</em></pre>
<div> <em>whatToSync</em> specifies what to sync, and must be a valid P4 file spec such as the ones used in the
<code>P4 sync</code> command (e.g. <code>//depot/MyProject/MyDir/...</code>,
<code>//depot/Proj2/main.c#2</code>, <code>@label</code>, etc...). For the <em>reverse</em> sync, the revision
range info is N.S., since the comparison is always done with the HEAD revision.</div>
<a name="reverseSync" id="reverseSync"></a><h4>Reverse sync</h4>
<div><code>p4ftpsync</code> may also be run in <em>reverse sync mode</em> (option -r). In this mode, the files
on the remote site are compared with the latest revision files in P4 and any change on the remote site is reported
into a new P4 <em>changelist</em> (optionally submitted at the end of the process). This mode is handy if some changes are
done directly on the remote site and you want to easily keep your P4 repository up to date. <em>Symbolic links</em>
are supported on the remote site but <strong>mapped to real files</strong> locally/in P4 named like the link.</div>
<a name="validOptions" id="validOptions"></a><h4>Valid options</h4>
<div class="optionCategory">Perforce options</div>
<div class="option">--p4Port <span style="font-weight:normal"><em>host</em>[:<em>port</em>]</span></div>
<div class="optionDesc">P4 host:port to use, default port 1666 [default: current config, see
<a href="#defaultValues" target="_self">Default values</a>]</div>
<div class="option">--p4User <span style="font-weight:normal"><em>userName</em></span></div>
<div class="optionDesc">P4 user name to use [default: see <a href="#defaultValues" target="_self">Default values</a>].</div>
<div class="option">--p4Passwd <span style="font-weight:normal"><em>password</em></span></div>
<div class="optionDesc">P4 user password [default: see <a href="#defaultValues" target="_self">Default values</a>].</div>
<div class="option">--p4Client <span style="font-weight:normal"><em>client</em></span></div>
<div class="optionDesc">P4 client to use as the mirror of the remote site [default: see
<a href="#defaultValues" target="_self">Default values</a>]. </div>
<div class="optionCategory">FTP options (for the remote site)</div>
<div class="option">--ftpHost <span style="font-weight:normal"><em>host</em>[:<em>port</em>]</span></div>
<div class="optionDesc">FTP host IP address, default port 21 [mandatory]</div>
<div class="option">--ftpUser <span style="font-weight:normal"><em>userName</em></span></div>
<div class="optionDesc">FTP user name [mandatory]</div>
<div class="option">--ftpPasswd <span style="font-weight:normal"><em>password</em></span></div>
<div class="optionDesc">FTP user password [default: will be prompted]</div>
<div class="option">--ftpRoot <span style="font-weight:normal"><em>rootRelativePath</em></span></div>
<div class="optionDesc">Path of the root folder of the site [mandatory]</div>
<div class="optionCategory">Normal sync (p4 to remote site) options</div>
<div class="option">-o, --scriptDir <span style="font-weight:normal"><em>localPath</em></span></div>
<div class="optionDesc">Directory in which to generate the Python update script in normal sync
[default: <code><em>thisProgDir</em>/p4FtpSyncScripts</code>]</div>
<div class="optionCategory">Reverse sync (remote site to P4) options</div>
<div class="option">-r, --reverse</div>
<div class="optionDesc">Reverse synchronization: Changes occurred on the remote site are detected, and
a P4 changelist is created but not submitted, unless option -s is specified.</div>
<div class="option">-s, --submit</div>
<div class="optionDesc">Submit the P4 changelist created for the changes detected. The default is to <strong>not</strong>
submit, so the changes can be reviewed before (manual) submit.</div>
<div class="option">-c, --comment <span style="font-weight:normal"><em>"COMMENT"</em></span></div>
<div class="optionDesc">Optional description for the changelist. A default will be generated if none is specified.</div>
<div class="option">-m, --mailto <span style="font-weight:normal"><em>addr1,addr2,...</em></span></div>
<div class="optionDesc">A list of email addresses to send a "file change report" to [default: don't send an email].
For now the mail server settings are globals in <code>p4ftpsync.py</code> !</div>
<div class="option">--smtpServer <span style="font-weight:normal"><em>host</em>[:<em>port</em>]</span></div>
<div class="optionDesc">SMTP server IP address to use for sending the above mail [default localhost:25]</div>
<div class="option">--smtpUser <span style="font-weight:normal"><em>userName</em></span></div>
<div class="optionDesc">SMTP user name if authentication is required [default: None]</div>
<div class="option">--smtpPasswd <span style="font-weight:normal"><em>password</em></span></div>
<div class="optionDesc">SMTP user password [default: None]</div>
<div class="option">--fromAddr <span style="font-weight:normal"><em>addr</em></span></div>
<div class="optionDesc">Email address to put in the 'From' field of the report emails [default: p4ftpsync@p4ftpsync.net -dummy!]</div>
<div class="optionCategory">Common sync. options</div>
<div class="option">-f, --force</div>
<div class="optionDesc">For <em>normal</em> sync: If specified, the target will be resynced even if supposedly up
to date (same as P4 sync option -f).<br />
For <em>reverse</em> sync: If specified, the file description <a href="#cache" title="File description Cache">cache</a>
will <strong>not</strong> be used and the file download & comparison will <strong>always</strong> be done.</div>
<div class="option">-t, --test</div>
<div class="optionDesc">Test/preview mode: For <em>normal</em> sync: do not really P4 sync the mirror client workspace,
generate the script but do not actually FTP.<br />
For <em>reverse</em> sync: do not create P4 changelist or copy files to client space.</div>
<div class="option">-x, --exclude <span style="font-weight:normal"><em>FILE1,FILE2,... | <strong>@</strong>FILE</em></span></div>
<div class="optionDesc">Exclude files from the list of updates to do (handy if some files must remain different locally
from remotely, e.g. config files, system files). FILEs are specified as a comma separated list (no spaces) of
<em>patterns</em>, or alternatively listed in a file (@FILE), one per line. <em>Patterns</em> are Python compatible
<em>regular expressions</em>. They are implicitely completed with a <code>^.*</code> on the left and a <code>$</code>
on the end (unless they already include them), meaning that if the <strong>end</strong> of the path of a file to sync
matches one of the patterns, then it will be excluded from the sync. The <code>(?i)</code> flag (IGNORECASE) can be added
to a pattern to make the match case-independent.<br /><br />
<strong>Examples of patterns</strong><br />
<code>.htaccess</code> (exclude the .htaccess file wherever it appears)<br />
<code>/A/B/f.txt</code> (exclude file f.txt located in folder /A/B/)<br />
<code>/log/.*</code> (exclude all files in directory log/)<br />
<code>/~.*</code> (exclude all files whose name starts with ~)<br />
<code>.pdf</code> (exclude all pdf files)<br />
<code>.pdf<strong>(?i)</strong></code> (ditto, but (?i) specifies to <strong>ignore case</strong>,
so .PDF matches too)<br /><br />
</div>
<div class="optionCategory">Misc options</div>
<div class="option">-v, --verbose</div>
<div class="optionDesc">Verbose: More trace/error messages on stdout.</div>
<div class="option">-V, --version</div>
<div class="optionDesc">Print p4ftpsync version on stdout and exit.</div>
<div class="option">-h, --help</div>
<div class="optionDesc">Print help message on stdout and exit.</div>
<a name="defaultValues" id="defaultValues"></a><h4>Default values</h4>
<div>p4ftpsync stores (in file <code>p4ftpsync.opt</code>) the last values used for options p4Client,
ftpHost, ftpUser and ftpRoot and uses them if no value is provided on the command line.
Options are stored twice: as associated to the specific target sync spec, and as generic
"last session defaults" (ftpRoot is not stored in the latter case).<br />
When trying to reload the values for a new session, p4ftpsync first attempts to reload the
target specific values, then the non specific ones.</div>
<a name="cache" id="cache"></a><h4>File description cache (reverse sync only)</h4>
<div> Remote file descriptions are saved on disk after a successful synchronization and used on
subsequent syncs to determine if a file has changed (different file descs), without having to
download the file, which can save a tremendous amount of time. There is a different cache for every
ftp host+user (file <code><em>user</em>@<em>ftpHost</em>.fdc</code>)<br />
On the <strong>first</strong> run of <code>p4ftpsync</code> for a given site, <strong>all</strong>
files from the remote site will be downloaded and compared to the files in the depot, quite a lengthy
and bandwith consuming operation ! However next iterations should use the cache and be considerably
faster (the only relatively long operation that can't be shortened is the determination of the current
file structure on the remote site). When the cache is missing or corrupted, the download of the entire
site (minus the exclusions) occurs again.<br />
The cache can be <strong>disabled</strong> with option -f, --force (it is then not used for the current
iteration but will still be saved for future use).
</div>
<a name="generatedScript" id="generatedScript"></a><h4>Generated FTP Script</h4>
<div>In normal sync, a <strong>Python script</strong> is created in the directory indicated by option -o
for the specified target (<em>whatToSync</em>). The script is named <em>what</em>_<em>date</em>.py
(with / and spaces replaced by _). It contains the set of FTP actions required to synchronize
the remote client (site) as requested, therefore its execution will perform the actual update,
which can prove very useful, should the FTP session fail before completion: executing the script will
<em>ftp</em> the changes again.
</div>
<a name="log" id="log"></a><h4>Log</h4>
<div> A file <code>p4ftpsync.log</code> is generated in <code>p4ftpsync</code>'s folder (always
<em>verbose</em>, unlike the on-screen trace which depends on option -v). Logs are rotated to
<code>p4ftpsync.log.<em>n</em>.zip</code> when they exceed a certain size (4MB by default).
</div>
<!-- LIMITATIONS -->
<a name="limitations" id="limitations"></a><h2>Limitations, Bugs, ToDo</h2>
<div>The most obvious limitations and todos I can think of right now are:
<ul>
<li>A limited support for <em>symbolic links</em>. Currently links are recognized on the remote site
but translated into normal files in P4 during reverse sync. That is, we tolerate that links
exist on the remote site but not locally and in P4. Since P4 supports symbolic links, it
could potentially be changed, though. (<em>Due to the comparison strategy used during reverse
sync, I'm not sure that the case where 2 links withing the site point to the same file is
correctly handled: it will create as many real files in p4</em>). </li>
<li>An uncertain support of <strong>older versions of the P4 server</strong> (<2005.1). This program probably works
with not-too-old versions of the server, but I wouldn't bet on it.</li>
<li>Support of <strong>all ftp servers</strong> is not guaranteed either. I tried on at least
3 different servers, but they are sometimes surprising in their use of error codes. </li>
<li>... and probably many other ones I don't think of ...</li>
</ul>
</div>
<div>Please report bugs or suggestions to <a href="javascript:location='mailto:\u0070\u0034\u0066\u0074\u0070\u0073\u0079\u006e\u0063\u0040\u0072\u0067\u0072\u0075\u0065\u0074\u002e\u006e\u0065\u0074';void 0">Richard Gruet</a>. I'd be happy to hear from you. I'll try to fix bugs
and implement suggestions if I think they can improve p4ftpsync (and are not to complex !). Don't hold your breath, though.
I'm quite busy.
</div>
<!-- LICENSE -->
<a name="license" id="license"></a><h2>License</h2>
<div>Copyright © 2004-2005 <a href="javascript:location='mailto:\u0070\u0034\u0066\u0074\u0070\u0073\u0079\u006e\u0063\u0040\u0072\u0067\u0072\u0075\u0065\u0074\u002e\u006e\u0065\u0074';void 0">Richard Gruet</a></div>
<div>Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee or royalty is hereby
granted, provided that the above copyright notice appear in all copies
and that both that copyright notice and this permission notice appear
in supporting documentation or portions thereof, including modifications,
that you make.</div>
<div>THE AUTHOR RICHARD GRUET DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !</div>
<div>In short: I publish this program in hope it can be useful to others. Use it as you wish but keep the copyright
intact, and don't hold me responsible for any problem you run into ;)</div>
</body>
</html>