Change SVNBinaryFileKeywordsPropertySetter into a FilePropertySetter.
[cvs2svn.git] / www / faq.html
blobc395cd15eaaedfa9da058dfa3a3c47a6eb3053f7
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5 <style type="text/css"> /* <![CDATA[ */
6 @import "tigris-branding/css/tigris.css";
7 @import "tigris-branding/css/inst.css";
8 /* ]]> */</style>
9 <link rel="stylesheet" type="text/css" media="print"
10 href="tigris-branding/css/print.css"/>
11 <script type="text/javascript" src="tigris-branding/scripts/tigris.js"></script>
12 <title>cvs2svn FAQ</title>
13 </head>
15 <body id="bodycol">
16 <div class="app">
18 <h1>cvs2svn FAQ</h1>
20 <p><strong>General:</strong></p>
22 <ol>
24 <li><a href="#incremental">Does cvs2svn support incremental
25 repository conversion?</a></li>
27 </ol>
30 <p><strong>Compatibility:</strong></p>
32 <ol>
34 <li><a href="#psyco">Does cvs2svn run under Psyco?</a></li>
36 </ol>
39 <p><strong>How-to:</strong></p>
41 <ol>
43 <li><a href="#repoaccess">How can I convert a CVS repository to
44 which I only have remote access?</a></li>
46 <li><a href="#oneatatime">How can I convert my CVS repository one
47 module at a time?</a></li>
49 <li><a href="#partialconversion">How can I convert part of a CVS
50 repository?</a></li>
52 <li><a href="#onetoone">How can I convert separate projects in my
53 CVS repository into a single Subversion repository?</a></li>
55 <li><a href="#automation">I have hundreds of subprojects to convert
56 and my options file is getting huge</a></li>
58 <li><a href="#inverted">How can I convert project <tt>foo</tt> so
59 that <tt>trunk/tags/branches</tt> are inside of
60 <tt>foo</tt>?</a></li>
62 <li><a href="#eol-fixup">How do I fix up end-of-line translation
63 problems?</a></li>
65 <li><a href="#path-symbol-transforms">I want a single project but
66 tag-rewriting rules that vary by subdirectory. Can this be
67 done?</a></li>
69 <li><a href="#cvsnt">How can I convert a CVSNT repository?</a></li>
71 <li><a href="#osxsetup">How do I get cvs2svn to run on OS X
72 10.5.5?</a></li>
74 </ol>
77 <p><strong>Problems:</strong></p>
79 <ol>
81 <li><a href="#atticprob">I get an error "A CVS repository cannot
82 contain both repo/path/file.txt,v and
83 repo/path/Attic/file.txt,v". What can I do?</a></li>
85 <li><a href="#rcsfileinvalid">I get an error "ERROR:
86 <i>filename</i>,v is not a valid ,v file."</a></li>
88 <li><a href="#gdbm-nfs">gdbm.error: (45, 'Operation not supported')</a></li>
90 <li><a href="#apple-single">When converting a CVS repository that
91 was used on a Macintosh, some files have incorrect contents in
92 SVN.</a></li>
94 <li><a href="#rcsmissing">Using cvs2svn 1.3.x, I get an error "The
95 command '['co', '-q', '-x,v', '-p1.1', '-kk',
96 '/home/cvsroot/myfile,v']' failed" in pass 8.</a></li>
98 <li><a href="#nonstandardntdb">Vendor branches created with
99 "cvs import -b &lt;branch number&gt;" are not correctly
100 handled.</a></li>
102 </ol>
105 <p><strong>Getting help:</strong></p>
107 <ol>
109 <li><a href="#gettinghelp">How do I get help?</a></li>
111 <li><a href="#reportingbugs">How do I report a bug?</a></li>
113 <li><a href="#testcase">How can I produce a useful test case?</a></li>
115 <li><a href="#commercialsupport">Does anybody offer commercial
116 support for cvs2svn/cvs2git conversions?</a></li>
118 </ol>
120 <hr />
123 <h2>General:</h2>
125 <h3><a name="incremental" title="#incremental">Does cvs2svn support
126 incremental repository conversion?</a></h3>
128 <p>No.</p>
130 <p>Explanation: During the transition from CVS to Subversion, it would
131 sometimes be useful to have the new Subversion repository track
132 activity in the CVS repository for a period of time until the final
133 switchover. This would require each conversion to determine what had
134 changed in CVS since the last conversion, and add those commits on top
135 of the Subversion repository.</p>
137 <p>Unfortunately, cvs2svn/cvs2git does <em>not</em> support
138 incremental conversions. With some work it would be possible to add
139 this feature, but it would be difficult to make it robust. The
140 trickiest problem is that CVS allows changes to the repository that
141 have retroactive effects (e.g., affecting parts of the history that
142 have already been converted).</p>
144 <p>Some conversion tools claim to support incremental conversions from
145 CVS, but as far as is known none of them are reliable.</p>
147 <p>Volunteers or sponsorship to add support for incremental
148 conversions to cvs2svn/cvs2git would be welcome.</p>
150 <hr />
153 <h2>Compatibility:</h2>
155 <h3><a name="psyco" title="#psyco">Does cvs2svn run under
156 Psyco?</a></h3>
158 <p>No.</p>
160 <p>Explanation: <a href="http://psyco.sourceforge.net/">Psyco</a> is a
161 python extension that can speed up the execution of Python code by
162 compiling parts of it into i386 machine code. Unfortunately, Psyco is
163 known <em>not</em> to run cvs2svn correctly (this was last tested with
164 the Psyco pre-2.0 development branch). When cvs2svn is run under
165 Psyco it crashes in <tt>OutputPass</tt> with an error message that
166 looks something like this:</p>
168 <pre>
169 cvs2svn_lib.common.InternalError: ID changed from 2 -> 3 for Trunk, r2
170 </pre>
172 <p>The Psyco team <a
173 href="https://sourceforge.net/tracker/?func=detail&amp;aid=2827082&amp;group_id=41036&amp;atid=429622">has
174 been informed about the problem</a>.</p>
176 <hr />
179 <h2>How-to:</h2>
181 <h3><a name="repoaccess" title="#repoaccess">How can I convert a CVS
182 repository to which I only have remote access?</a></h3>
184 <p>cvs2svn requires direct, filesystem access to a copy of the CVS
185 repository that you want to convert. The reason for this requirement
186 is that cvs2svn directly parses the <tt>*,v</tt> files that make up
187 the CVS repository.</p>
189 <p>Many remote hosting sites provide access to backups of your CVS
190 repository, which could be used for a cvs2svn conversion. For
191 example:</p>
193 <ul>
194 <li><a href="http://sourceforge.net">SourceForge</a> allows CVS
195 content to be accessed via
196 <a href="http://sourceforge.net/docs/E04/en/#rsync">rsync</a>. In
197 fact, they provide <a
198 href="http://sourceforge.net/apps/trac/sourceforge/wiki/SVN%20adminrepo#Usingcvs2svntocreateaSVNdumpfilefromCVScontent">complete instructions</a>
199 for migrating a SourceForge project from CVS to SVN.</li>
200 <li>...<i>(other examples welcome)</i></li>
201 </ul>
203 <p>If your provider does not provide any way to download your CVS
204 repository, there are two known tools that claim to be able to
205 clone a CVS repository via the CVS protocol:</p>
207 <ul>
209 <li><a href="http://samba.org/ftp/tridge/rtc/cvsclone.l">cvsclone</a></li>
211 <li><a href="http://cvs.m17n.org/~akr/cvssuck/">CVSsuck</a></li>
213 </ul>
215 <p>It should be possible to use one of these tools to fetch a copy of
216 your CVS repository from your provider, then to use cvs2svn to convert
217 the copy. However, the developers of cvs2svn do not have any
218 experience with these tools, so you are on your own here. If you try
219 one of them, please tell us about your experience on the <a
220 href="mailto:users@cvs2svn.tigris.org">users mailing list</a>.</p>
223 <h3><a name="oneatatime" title="#oneatatime">How can I convert my CVS
224 repository one module at a time?</a></h3>
226 <p>If you need to convert certain CVS modules (in one large
227 repository) to Subversion <b>now</b> and other modules <b>later</b>,
228 you may want to convert your repository one module at a time. This
229 situation is typically encountered in large organizations where each
230 project has a separate lifecycle and schedule, and a one-step
231 conversion process is not practical.
232 </p>
234 <p>First you have to decide whether you want to put your converted
235 projects into a single Subversion repositories or multiple ones. This
236 decision mostly depends on the degree of coupling between the projects
237 and is beyond the scope of this FAQ. See <a
238 href="http://svnbook.red-bean.com/en/1.2/svn.reposadmin.projects.html#svn.reposadmin.projects.chooselayout">the
239 Subversion book</a> for a discussion of repository organization.
240 </p>
242 <p>If you decide to convert your projects into separate Subversion
243 repositories, then please follow the instructions in <a
244 href="#partialconversion">How can I convert part of a CVS
245 repository?</a> once for each repository.
246 </p>
248 <p>If you decide to put more than one CVS project into a single
249 Subversion repository, then please follow the instructions in <a
250 href="#onetoone">How can I convert separate projects in my CVS
251 repository into a single Subversion repository?</a>.
252 </p>
255 <h3><a name="partialconversion" title="#partialconversion">How can I
256 convert part of a CVS repository?</a></h3>
258 <p>This is easy: simply run cvs2svn normally, passing it the path of
259 the project subdirectory within the CVS repository. Since cvs2svn
260 ignores any files outside of the path it is given, other projects
261 within the CVS repository will be excluded from the conversion.
262 </p>
264 <p>Example: You have a CVS repository at path <tt>/path/cvsrepo</tt>
265 with projects in subdirectories <tt>/path/cvsrepo/foo</tt> and
266 <tt>/path/cvsrepo/bar</tt>, and you want to create a new Subversion
267 repository at <tt>/path/foo-svn</tt> that includes only the
268 <tt>foo</tt> project:
269 </p>
271 <pre>
272 $ cvs2svn -s /path/foo-svn /path/cvsrepo/foo
273 </pre>
276 <h3><a name="onetoone" title="#onetoone">How can I convert separate
277 projects in my CVS repository into a single Subversion
278 repository?</a></h3>
280 <p>cvs2svn supports multiproject conversions, but you have to use the
281 <a href="cvs2svn.html#options-file-method">options file method</a> to
282 start the conversion. In your options file, you simply call
283 <tt>run_options.add_project()</tt> once for each sub-project in your
284 repository. For example, if your CVS repository has the layout:</p>
286 <pre>
287 /project_a
288 /project_b
289 </pre>
291 <p>and you want your Subversion repository to be laid out like this:</p>
293 <pre>
294 project_a/
295 trunk/
297 branches/
299 tags/
301 project_b/
302 trunk/
304 branches/
306 tags/
308 </pre>
310 <p>then you need to have a section like this in your options file:</p>
312 <pre>
313 run_options.add_project(
314 'my/cvsrepo/project_a',
315 trunk_path='project_a/trunk',
316 branches_path='project_a/branches',
317 tags_path='project_a/tags',
318 symbol_transforms=[
319 #...whatever...
321 symbol_strategy_rules=[
322 #...whatever...
325 run_options.add_project(
326 'my/cvsrepo/project_b',
327 trunk_path='project_b/trunk',
328 branches_path='project_b/branches',
329 tags_path='project_b/tags',
330 symbol_transforms=[
331 #...whatever...
333 symbol_strategy_rules=[
334 #...whatever...
337 </pre>
340 <h3><a name="automation" title="#automation">I have hundreds of
341 subprojects to convert and my options file is getting
342 huge</a></h3>
344 <p>The options file is Python code, executed by the Python
345 interpreter. This makes it easy to automate parts of the
346 configuration process. For example, to add many subprojects, you can
347 write a Python loop:</p>
349 <pre>
350 projects = ['A', 'B', 'C', ...etc...]
352 cvs_repo_main_dir = r'test-data/main-cvsrepos'
353 for project in projects:
354 run_options.add_project(
355 cvs_repo_main_dir + '/' + project,
356 trunk_path=(project + '/trunk'),
357 branches_path=(project + '/branches'),
358 tags_path=(project + '/tags'),
359 # ...
361 </pre>
363 <p>or you could even read the subprojects directly from the CVS
364 repository:</p>
366 <pre>
367 import os
368 cvs_repo_main_dir = r'test-data/main-cvsrepos'
369 projects = os.listdir(cvs_repo_main_dir)
371 # Probably you don't want to convert CVSROOT:
372 projects.remove('CVSROOT')
374 for project in projects:
375 # ...as above...
376 </pre>
379 <h3><a name="inverted" title="#inverted">How can I convert project
380 <tt>foo</tt> so that <tt>trunk/tags/branches</tt> are inside of
381 <tt>foo</tt>?</a></h3>
383 <p>If <tt>foo</tt> is the only project that you want to convert,
384 then either run cvs2svn like this:</p>
386 <pre>
387 $ cvs2svn --trunk=foo/trunk --branches=foo/branches --tags=foo/tags CVSREPO/foo
388 </pre>
390 <p>or use an options file that defines a project like this:</p>
392 <pre>
393 run_options.add_project(
394 'my/cvsrepo/foo',
395 trunk_path='foo/trunk',
396 branches_path='foo/branches',
397 tags_path='foo/tags',
398 symbol_transforms=[
399 #...whatever...
401 symbol_strategy_rules=[
402 #...whatever...
405 </pre>
407 <p>If <tt>foo</tt> is not the only project that you want to convert,
408 then you need to do a multiproject conversion; see <a
409 href="#onetoone">How can I convert separate projects in my CVS
410 repository into a single Subversion repository?</a> for more
411 information.</p>
414 <h3><a name="eol-fixup" title="#eol-fixup">How do I fix up end-of-line
415 translation problems?</a></h3>
417 <p>Warning: cvs2svn's handling of end-of-line options changed
418 between version 1.5.x and version 2.0.x. <strong>This
419 documentation applies to version 2.0.x and later.</strong> The
420 documentation applying to an earlier version can be found in the
421 <tt>www</tt> directory of that release of cvs2svn.</p>
423 <p>Starting with version 2.0, the default behavior of cvs2svn is to
424 treat all files as binary except those explicitly determined to be
425 text. (Previous versions treated files as text unless they were
426 determined to be binary.) This behavior was changed because,
427 generally speaking, it is safer to treat a text file as binary
428 than vice versa.</p>
430 <p>However, it is often preferred to set
431 <tt>svn:eol-style=native</tt> for text files, so that their
432 end-of-file format is converted to that of the client platform
433 when the file is checked out. This section describes how to
434 get the settings that you want.</p>
436 <p>If a file is marked as binary in CVS (with <tt>cvs admin
437 -kb</tt>, then cvs2svn will always treat the file as binary. For
438 other files, cvs2svn has a number of options that can help choose
439 the correct end-of-line translation parameters during the
440 conversion:</p>
442 <table border="1" cellpadding="10" cellspacing="3" width="80%">
444 <tr>
445 <td align="right"><tt>--auto-props=FILE</tt></td>
446 <td>
448 <p>Set arbitrary Subversion properties on files based on the
449 auto-props section of a file in svn config format. The
450 auto-props file might have content like this:</p>
452 <pre>
453 [auto-props]
454 *.txt = svn:mime-type=text/plain;svn:eol-style=native
455 *.doc = svn:mime-type=application/msword;!svn:eol-style
456 </pre>
458 <p>This option can also be used in combination with
459 <tt>--eol-from-mime-type</tt>.</p>
461 <p>To force end-of-line translation off, use a setting of
462 the form <tt>!svn:eol-style</tt> (with a leading
463 exclamation point).</p>
465 </td>
466 </tr>
468 <tr>
469 <td align="right"><tt>--mime-types=FILE</tt></td>
470 <td><p>Specifies an Apache-style mime.types file for setting
471 files' <tt>svn:mime-type</tt> property based on the file
472 extension. The mime-types file might have content like
473 this:</p>
474 <pre>
475 text/plain txt
476 application/msword doc
477 </pre>
478 <p>This option only has an effect on <tt>svn:eol-style</tt>
479 if it is used in combination with
480 <tt>--eol-from-mime-type</tt>.</p></td>
481 </tr>
483 <tr>
484 <td align="right"><tt>--eol-from-mime-type</tt></td>
485 <td>Set <tt>svn:eol-style</tt> based on the file's mime type
486 (if known). If the mime type starts with "<tt>text/</tt>",
487 then the file is treated as a text file; otherwise, it is
488 treated as binary. This option is useful in combination with
489 <tt>--auto-props</tt> or <tt>--mime-types</tt>.</td>
490 </tr>
492 <tr>
493 <td align="right"><tt>--default-eol=STYLE</tt></td>
494 <td>Usually cvs2svn treats a file as binary unless one of the
495 other rules determines that it is not binary and it is not
496 marked as binary in CVS. But if this option is specified,
497 then cvs2svn uses the specified style as the default. STYLE
498 can be 'binary' (default), 'native', 'CRLF', 'LF', or 'CR'.
499 If you have been diligent about annotating binary files in
500 CVS, or if you are confident that the above options will
501 catch all of your binary files, then
502 <tt>--default-style=native</tt> should give good
503 results.</td>
504 </tr>
506 </table>
508 <p>If you don't use any of these options, then cvs2svn will not
509 arrange any line-end translation whatsoever. The file contents in
510 the SVN repository should be the same as the contents you would
511 get if checking out with CVS on the machine on which cvs2svn is
512 run. This also means that the EOL characters of text files will
513 be the same no matter where the SVN data are checked out (i.e.,
514 not translated to the checkout machine's EOL format).</p>
516 <p>To do a better job, you can use <tt>--auto-props</tt>,
517 <tt>--mime-types</tt>, and <tt>--eol-from-mime-type</tt> to
518 specify exactly which properties to set on each file based on its
519 filename.</p>
521 <p>For total control over setting properties on files, you can use
522 the <a
523 href="cvs2svn.html#options-file-method"><tt>--options</tt>-file
524 method</a> and write your own <tt>RevisionPropertySetter</tt> in
525 Python. For example,</p>
526 <pre>
527 from cvs2svn_lib.property_setters import RevisionPropertySetter
529 class MyPropertySetter(RevisionPropertySetter):
530 def set_properties(self, s_item):
531 if s_item.cvs_rev.cvs_file.cvs_path.startswith('path/to/funny/files/'):
532 s_item.svn_props['svn:mime-type'] = 'text/plain'
533 s_item.svn_props['svn:eol-style'] = 'CRLF'
535 ctx.revision_property_setters.append(MyPropertySetter())
536 </pre>
537 <p>See the file <tt>cvs2svn_lib/property_setters.py</tt> for more
538 examples.</p>
541 <h3><a name="path-symbol-transforms" title="#path-symbol-transforms">I
542 want a single project but tag-rewriting rules that vary by
543 subdirectory. Can this be done?</a></h3>
545 <p>This is an example of how the cvs2svn conversion can be
546 customized using Python.</p>
548 <p>Suppose you want to write symbol transform rules that are more
549 complicated than "replace REGEXP with PATTERN". This can easily
550 be done by adding just a little bit of Python code to your <a
551 href="cvs2svn.html#options-file-method">options file</a>.</p>
553 <p>When a symbol is encountered, cvs2svn iterates through the list
554 of <tt>SymbolTransform</tt> objects defined for the project. For
555 each one, it calls <tt>symbol_transform.transform(cvs_file,
556 symbol_name, revision)</tt>. That method can return
557 any legal symbol name, which will be used in the conversion
558 instead of the original name.</p>
560 <p>To use this feature, you will have to use an <a
561 href="cvs2svn.html#options-file-method">options file</a> to start
562 the conversion. You then write a new SymbolTransform class that
563 inherits from RegexpSymbolTransform but checks the path before
564 deciding whether to transform the symbol. Add the following to
565 your options file:</p>
567 <pre>
568 from cvs2svn_lib.symbol_transform import RegexpSymbolTransform
570 class MySymbolTransform(RegexpSymbolTransform):
571 def __init__(self, path, pattern, replacement):
572 """Transform only symbols that occur within the specified PATH."""
574 self.path = path
575 RegexpSymbolTransform.__init__(self, pattern, replacement)
577 def transform(self, cvs_file, symbol_name, revision):
578 # Is the file is within the path we are interested in?
579 if cvs_file.cvs_path.startswith(path + '/'):
580 # Yes -> Allow RegexpSymbolTransform to transform the symbol:
581 return RegexpSymbolTransform.transform(
582 self, cvs_file, symbol_name, revision)
583 else:
584 # No -> Return the symbol unchanged:
585 return symbol_name
587 # Note that we use a Python loop to fill the list of symbol_transforms:
588 symbol_transforms = []
589 for subdir in ['project1', 'project2', 'project3']:
590 symbol_transforms.append(
591 MySymbolTransform(
592 subdir,
593 r'release-(\d+)_(\d+)',
594 r'%s-release-\1.\2' % subdir))
596 # Now register the project, using our own symbol transforms:
597 run_options.add_project(
598 'your_cvs_path',
599 trunk_path='trunk',
600 branches_path='branches',
601 tags_path='tags',
602 symbol_transforms=symbol_transforms))
603 </pre>
605 <p>This example causes any symbol under "project1" that looks like
606 "release-3_12" to be transformed into a symbol named
607 "project1-release-3.12", whereas if the same symbol appears under
608 "project2" it will be transformed into
609 "project2-release-3.12".</p>
612 <h3><a name="cvsnt" title="#cvsnt">How can I convert a CVSNT
613 repository?</a></h3>
615 <p><a href="http://www.cvsnt.org/">CVSNT</a> is a version control
616 system that started out by adding support for running CVS under
617 Windows NT. Since then it has made numerous extensions to the RCS
618 file format, to the point where CVS compatibility does not imply
619 CVSNT compatibility with any degree of certainty.</p>
621 <p>cvs2svn <em>might</em> happen to successfully convert a CVSNT
622 repository, especially if the repository has never had any
623 CVSNT-only features used on it, but <b>this use is not supported
624 and should not be expected to work</b>.</p>
626 <p>If you want to experiment with converting a CVSNT repository,
627 then please consider the following suggestions:</p>
629 <ul>
630 <li>Use cvs2svn's <tt>--use-cvs</tt> option.</li>
632 <li>Use CVSNT's version of the <tt>cvs</tt> executable (i.e.,
633 ensure that the first <tt>cvs</tt> program in your $PATH is the
634 one that came with CVSNT).</li>
636 <li>Carefully check the result of the conversion before you rely
637 on it, <em>even if the conversion completed without any
638 errors or warnings</em>.</li>
640 </ul>
642 <p>Patches to support the conversion of CVSNT repositories would, of
643 course, be welcome.</p>
646 <h3><a name="osxsetup" title="#osxsetup">How do I get cvs2svn to run
647 on OS X 10.5.5?</a></h3>
649 <p>Attempting to run cvs2svn on a standard OS X 10.5.5 installation
650 yields the following error:</p>
652 <blockquote> <p> ERROR: cvs2svn uses the anydbm package, which depends on
653 lower level dbm libraries. Your system has dbm, with which cvs2svn is
654 known to have problems. To use cvs2svn, you must install a Python dbm
655 library other than dumbdbm or dbm. See <a
656 href="http://python.org/doc/current/lib/module-anydbm.html">http://python.org/doc/current/lib/module-anydbm.html</a>
657 for more information. </p> </blockquote>
659 <p>The problem is that the standard distribution of python on OS X
660 10.5.5 does not include any other dbm libraries other than the
661 standard dbm. In order for cvs2svn to work, we need to install the
662 gdbm library, in addition to a new version of python that enables the
663 python gdbm module.</p>
665 <p>The precompiled versions of python for OS X available from
666 python.org or activestate.com (currently version 2.6.2) do not have
667 gdbm support turned on. To check for gdbm support, check for the
668 library module (<code>libgdmmodule.so</code>) within the python
669 installation.</p>
671 <p>Here is the procedure for a successful installation of cvs2svn and
672 all supporting libs:</p>
674 <ol>
676 <li>Download the gdbm-1.8.3 (or greater) source, unarchive and
677 change directory to gdbm-1.8.3. We need to install the gdbm
678 libraries so python's gdbm module can use them.
680 <ol>
682 <li>Type <code>./configure</code></li>
684 <li>Edit "Makefile" so that the owner and group are not the
685 non-existing "bin" owner and group by changing
687 <pre>
688 BINOWN = bin
689 BINGRP = bin
690 </pre>
692 <pre>
693 BINOWN = root
694 BINGRP = admin
695 </pre>
697 </li>
699 <li>Type "make"</li>
701 <li>Type "sudo make install"</li>
703 </ol>
705 </li>
707 <li>Download the Python2.6 (or greater) source, unarchive, and
708 change directory to Python2.6. We need to enable python gdbm
709 support which is not enabled in the default OS X 10.5.5 installation
710 of python, as the gdbm libs are not included. However, we just
711 installed the gdbm libs in step 1, so we can now compile python with
712 gdbm support.
714 <ol>
716 <li>Edit the file "Modules/Setup" by uncommenting the line which
717 links against gdbm by changing
719 <pre>
720 #gdbm gdbmmodule.c -I/usr/local/include -L/usr/local/lib -lgdbm
721 </pre>
723 <pre>
724 gdbm gdbmmodule.c -I/usr/local/include -L/usr/local/lib -lgdbm
725 </pre>
726 </li>
728 <li>Edit the file "Modules/Setup" by uncommenting the line to
729 create shared libs by changing
731 <pre>
732 #*shared*
733 </pre>
735 <pre>
736 *shared*
737 </pre>
738 </li>
740 <li>Type <code>./configure --enable-framework
741 --enable-universalsdk</code> in the top-level
742 Python2.6 directory. This will configure the installation of
743 python as a shared OS X framework, and usable with OS X GUI
744 frameworks and SDKs. You may have problems building if you don't
745 have the SDKs that support the PPC platform. If you do, just
746 specify <code>--disable-universalsdk</code>.
747 By default, python will be installed in
748 "/Library/Frameworks/Python.framework", which is what we
749 want.</li>
751 <li>Type <code>make</code></li>
753 <li>Type <code>sudo make install</code></li>
755 <li>Type <code>cd /usr/local/bin; sudo ln -s python2.6 python</code></li>
757 <li>Make sure "/usr/local/bin" is at the front of your search path
758 in ~/.profile or ~/.bashrc etc.</li>
760 <li>Type <code>source ~/.profle</code> or <code>source
761 ~/.bashrc</code> etc. or alternatively, just open a new shell
762 window. When you type <code>which python</code> it should give
763 you the new version in "/usr/local/bin" <strong>not</strong> the
764 one in "/usr/bin".</li>
766 </ol>
768 </li>
770 <li>Download the cvs2svn-2.2.0 (or greater) source, unarchive and
771 change directory to cvs2svn-2.2.0. Many people can't get cvs2svn to
772 work except in the installation directory. The reason for this is
773 that the installation places copies of cvs2svn, cvs2svn_libs, and
774 cvs2svn_rcsparse in the /Library/Frameworks/Python.framework
775 hierarchy. All we need to do is make a link in /usr/local/bin
776 pointing to the location of cvs2svn in the python framework
777 hierarchy. And for good measure we also make links to the lib and
778 include directories:
780 <ol>
782 <li>Type <code>sudo make install</code></li>
784 <li>Create the required links by typing the following:
786 <pre>
787 sudo ln -s /Library/Frameworks/Python.framework/Versions/2.6/bin/cvs2svn /usr/local/bin/cvs2svn
788 sudo ln -s /Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6 /usr/local/lib/python2.6
789 sudo ln -s /Library/Frameworks/Python.framework/Versions/2.6/include/python2.6 /usr/local/include/python2.6
790 </pre>
792 </li>
794 </ol>
796 </li>
798 </ol>
800 <p>The installation is complete. Change directory out of the
801 cvs2svn-2.2.0 installation directory, and you should be able to run
802 cvs2svn. Be careful *not* to copy the version of cvs2svn in the
803 cvs2svn-2.2.0 installation directory to /usr/local/bin, as this has a
804 different python environment setting at the top of the file than the
805 one that was installed in the /Library/Frameworks/Python.framework
806 hierarchy. Follow the instructions exactly, and it should work.
807 </p>
810 <hr />
812 <h2>Problems:</h2>
814 <h3><a name="atticprob" title="#atticprob">I get an error "A CVS
815 repository cannot contain both repo/path/file.txt,v and
816 repo/path/Attic/file.txt,v". What can I do?</a></h3>
818 <p>Background: Normally, if you have a file called
819 <tt>path/file.txt</tt> in your project, CVS stores its history in a
820 file called <tt>repo/path/file.txt,v</tt>. But if <tt>file.txt</tt>
821 is deleted on the main line of development, CVS moves its history file
822 to a special <tt>Attic</tt> subdirectory:
823 <tt>repo/path/Attic/file.txt,v</tt>. (If the file is recreated, then
824 it is moved back out of the <tt>Attic</tt> subdirectory.) Your
825 repository should never contain both of these files at the same
826 time.</p>
828 <p>This cvs2svn error message thus indicates a mild form of corruption
829 in your CVS repository. The file has two conflicting histories, and
830 even CVS does not know the correct history of <tt>path/file.txt</tt>.
831 The corruption was probably created by using tools other than CVS to
832 backup or manipulate the files in your repository. With a little work
833 you can learn more about the two histories by viewing each of the
834 <tt>file.txt,v</tt> files in a text editor.</p>
836 <p>There are four straightforward approaches to fixing the repository
837 corruption, but each has potential disadvantages. Remember to <b>make
838 a backup</b> before starting. Never run cvs2svn on a live CVS
839 repository--always work on a copy of your repository.</p>
841 <ol>
842 <li>Restart the conversion with the
843 <tt>--retain-conflicting-attic-files</tt> option. This causes the
844 non-attic and attic versions of the file to be converted
845 separately, with the <tt>Attic</tt> version stored to a new
846 subdirectory as <tt>path/Attic/file.txt</tt>. This approach
847 avoids losing any history, but by moving the <tt>Attic</tt>
848 version of the file to a different subdirectory it might cause
849 historical revisions to be broken.</li>
851 <li>Remove the <tt>Attic</tt> version of the file and restart the
852 conversion. Sometimes it represents an old version of the file
853 that was deleted long ago, and it won't be missed. But this
854 completely discards one of the file's histories, probably causing
855 <tt>file.txt</tt> to be missing in older historical revisions.
856 (For what it's worth, this is probably how CVS would behave in
857 this situation.)
859 <pre>
860 # You did make a backup, right?
861 $ rm repo/path/Attic/file.txt,v
862 </pre></li>
864 <li>Remove the non-<tt>Attic</tt> version of the file and restart
865 the conversion. This might be appropriate if the
866 non-<tt>Attic</tt> version has less important content than the
867 <tt>Attic</tt> version. But this completely discards one of the
868 file's histories, probably causing <tt>file.txt</tt> to be missing
869 in recent historical revisions.
871 <pre>
872 # You did make a backup, right?
873 $ rm repo/path/file.txt,v
874 </pre></li>
876 <li>Rename the non-<tt>Attic</tt> version of the file and restart
877 the conversion. This avoids losing history, but it changes the
878 name of the non-<tt>Attic</tt> version of the file to
879 <tt>file-not-from-Attic.txt</tt> whenever it appeared, and might
880 thereby cause revisions to be broken.
882 <pre>
883 # You did make a backup, right?
884 $ mv repo/path/file.txt,v repo/path/file-not-from-Attic.txt,v
885 </pre></li>
887 </ol>
889 <p>If you run cvs2svn on a case-insensitive operating system, it is
890 possible to get this error even if the filename of the file in
891 Attic has different case than the one out of the Attic. This could
892 happen, for example, if the CVS repository was served from a
893 case-sensitive operating system at some time. A workaround for this
894 problem is to copy the CVS repository to a case-sensitive operating
895 system and convert it there.
896 </p>
899 <h3><a name="rcsfileinvalid" title="#rcsfileinvalid">I get an error
900 "ERROR: <i>filename</i>,v is not a valid ,v file."</a></h3>
902 <p>The named file is corrupt in some way. (Corruption is surprisingly
903 common in CVS repositories.) It is likely that even CVS has problems
904 with this file; try checking out the head revision, revision 1.1, and
905 the tip revision on each branch of this file; probably one or more of
906 them don't work.</p>
908 <p>Here are some options:</p>
910 <ol>
912 <li>Omit this file from the conversion (by making a copy of your
913 repository, deleting this file from the copy, then converting from
914 the copy).</li>
916 <li>Restore an older copy of this file from backups, if you have
917 backups from before it was corrupted.</li>
919 <li>Hand-fix the file as best you can by opening it in a binary
920 editor and trying to put it back in RCS file format (documented in
921 the rcsfile(5) manpage). Often it is older revisions that are
922 affected by corruption; you might need to delete some old
923 revisions to salvage newer ones.</li>
925 </ol>
928 <h3><a name="gdbm-nfs" title="#gdbm-nfs">gdbm.error: (45, 'Operation
929 not supported')</a></h3>
931 <p>This has been reported to be caused by trying to create gdbm
932 databases on an NFS partition. Apparently gdbm does not support
933 databases on NFS partitions. The workaround is to use the
934 <tt>--tmpdir</tt> option to choose a local partition for cvs2svn to
935 write its temporary files.</p>
938 <h3><a name="apple-single" title="#apple-single">When converting a CVS
939 repository that was used on a Macintosh, the contents of some
940 files are incorrect in SVN.</a></h3>
942 <p>Some Macintosh CVS clients use a nonstandard trick to store the
943 resource fork of files in CVS: instead of storing the file contents
944 directly, store an <a
945 href="http://rfc.net/rfc1740.html">AppleSingle</a> data stream
946 containing both the data fork and resource fork. When checking the
947 file out, the client unpacks the AppleSingle data and writes the two
948 forks separately to disk. By default, cvs2svn treats the file
949 contents literally, so when you check the file out of Subversion, the
950 file contains the combined data in AppleSingle format rather than only
951 the data fork of the file as expected.</p>
953 <p>Subversion does not have any special facilities for dealing with
954 Macintosh resource forks, so there is nothing cvs2svn can do to
955 preserve both forks of your data. However, sometimes the resource
956 fork is not needed. If you would like to discard the resource fork
957 and only record the data fork in Subversion, then start your
958 conversion using the <a
959 href="cvs2svn.html#options-file-method">options file method</a> and
960 set the following option to <tt>True</tt> in your options file:</p>
962 <pre>
963 ctx.decode_apple_single = True
964 </pre>
966 <p>There is more information about this option in the comments in
967 <tt>cvs2svn-example.options</tt>.</p>
970 <h3><a name="rcsmissing" title="#installrcs">Using cvs2svn 1.3.x, I
971 get an error "The command '['co', '-q', '-x,v', '-p1.1', '-kk',
972 '/home/cvsroot/myfile,v']' failed" in pass 8.</a></h3>
974 <p><i>What are you using cvs2svn version 1.3.x for anyway?
975 Upgrade!</i></p>
977 <p>But if you must, either install RCS, or ensure that CVS is
978 installed and use cvs2svn's <a
979 href="cvs2svn.html#use-cvs"><tt>--use-cvs</tt></a> option.</p>
982 <h3><a name="nonstandardntdb" title="#nonstandardntdb">Vendor
983 branches created with "cvs import -b &lt;branch number&gt;"
984 are not correctly handled.</a></h3>
986 <p>Normally, people using "cvs import" don't specify the
987 "-b" flag. cvs2svn handles this normal case fine.</p>
989 <p>If you have a file which has an <i>active</i> vendor branch, i.e.
990 there have never been any trunk commits but only "cvs imports" onto
991 the vendor branch, then cvs2svn will handle this fine. (Even if
992 you've used the "-b" option to specify a non-standard branch
993 number).</p>
995 <p>If you've used "cvs import -b &lt;branch number&gt;", you didn't
996 specify the standard CVS vendor branch number of 1.1.1, and there
997 has since been a commit on trunk (either a modification or delete),
998 then your history has been damaged. This isn't cvs2svn's fault.
999 CVS simply doesn't record the branch number of the old vendor branch,
1000 it assumes it was 1.1.1. You will even get the wrong results from
1001 "cvs checkout -D" with a date when the vendor branch was active.</p>
1003 <p>Symptoms of this problem can include:</p>
1005 <ul>
1006 <li>cvs2svn refusing to let you exclude the vendor branch, because
1007 some other branch depends on it</li>
1008 <li>if you did more than one import onto the vendor branch, then
1009 your SVN history "missing" one of the changes on trunk (though
1010 the change will be on the vendor branch).</li>
1011 </ul>
1013 <p>(Note: There are other possible causes for these symptoms, don't
1014 assume you have a non-standard vendor branch number just because
1015 you see these symptoms).</p>
1017 <p>The way to solve this problem is to renumber the vendor branch to
1018 the standard 1.1.1 branch number. This has to be done before you run
1019 cvs2svn. To help you do this, there is the "renumber_branch.py"
1020 script in the "contrib" directroy of the cvs2svn distribution.</p>
1022 <p>The typical usage, assuming you used "cvs import -b 1.1.2 ..."
1023 to create your vendor branch, is:</p>
1024 <pre>
1025 contrib/renumber_branch.py 1.1.2 1.1.1 repos/dir/file,v
1026 </pre>
1027 <p>You should only run this on a <b>copy</b> of your CVS repository,
1028 as it edits the repository in-place. You can fix a single file or a
1029 whole directory tree at a time.</p>
1031 <p>The script will check that the 1.1.1 branch doesn't already exist;
1032 if it does exist then it will fail with an error message.</p>
1036 <h2>Getting help:</h2>
1038 <h3><a name="gettinghelp" title="#gettinghelp">How do I get
1039 help?</a></h3>
1041 <p>There are several sources of help for cvs2svn:</p>
1043 <ul>
1045 <li>The <a href="cvs2svn.html">user manual</a> not only describes
1046 how to run cvs2svn, but also discusses some limitations, pitfalls,
1047 and conversion strategies. Please note that the <a
1048 href="http://cvs2svn.tigris.org/cvs2svn.html">online manual</a>
1049 describes the latest "bleeding edge" trunk version of the software,
1050 which may be different than the version that you are using.</li>
1052 <li>The <a href="faq.html">frequently asked questions (FAQ) list</a>
1053 is the document that you are now reading. Please make sure you've
1054 scanned through the list of topics to see if your question is
1055 already answered.</li>
1057 <li>The <a
1058 href="http://cvs2svn.tigris.org/servlets/ProjectMailingListList">mailing
1059 list archives</a>. Maybe your question has
1060 been discussed on either the <tt>user@cvs2svn.tigris.org</tt> or
1061 <tt>dev@cvs2svn.tigris.org</tt> mailing list.</li>
1063 <li>If you need help with running cvs2svn or problems converting
1064 your repository, the <a
1065 href="mailto:users@cvs2svn.tigris.org"><tt>users@cvs2svn.tigris.org</tt></a>
1066 mailing list is the first place to send inquiries. Please <a
1067 href="http://cvs2svn.tigris.org/servlets/ProjectMailingListList">subscribe</a>
1068 to the list so that you can follow ensuing discussions.</li>
1070 <li>You can also ask questions on IRC at <a
1071 href="irc://irc.freenode.net/"><tt>irc.freenode.net</tt></a>,
1072 channel <tt>#cvs2svn</tt>.</li>
1074 <li>If you think you have found a bug, please refer to <a
1075 href="#reportingbugs">"How do I report a bug?"</a></li>
1077 </ul>
1080 <h3><a name="reportingbugs" title="#reportingbugs">How do I report a
1081 bug?</a></h3>
1083 <p>cvs2svn is an open source project that is largely developed and
1084 supported by volunteers in their free time. Therefore please try to
1085 help out by reporting bugs in a way that will enable us to help you
1086 efficiently.</p>
1088 <p>The first question is whether the problem you are experiencing is
1089 caused by a cvs2svn bug at all. A large fraction of reported "bugs"
1090 are caused by problems with the user's CVS repository, especially mild
1091 forms of repository corruption or <a href="#cvsnt">trying to convert a
1092 CVSNT repository with cvs2svn</a>. Please also double-check the <a
1093 href="cvs2svn.html">manual</a> to be sure that you are using the
1094 command-line options correctly.</p>
1096 <p>A good way to localize potential repository corruption is to use
1097 the <tt>shrink_test_case.py</tt> script (which is located in the
1098 <tt>contrib</tt> directory of the cvs2svn source tree). This script
1099 tries to find the minimum subset of files in your repository that
1100 still shows the same problem. <b>Warning: Only apply this script to a
1101 backup copy of your repository, as it destroys the repository that it
1102 operates on!</b> Often this script can narrow the problem down to a
1103 single file which, as often as not, is corrupt in some way. Even if
1104 the problem is not in your repository, the shrunk-down test case will
1105 be useful for reporting the bug. Please see <a href="#testcase">"How
1106 can I produce a useful test case?"</a> and the comments at the top of
1107 <tt>shrink_test_case.py</tt> for information about how to use this
1108 script.</p>
1110 <p>Assuming that you still think you have found a bug, the next step
1111 is to investigate whether the bug is already known. Please look
1112 through the <a
1113 href="http://cvs2svn.tigris.org/issue_tracker.html">issue tracker</a>
1114 for bugs that sound familiar. If the bug is already known, then there
1115 is no need to report it (though possibly you could contribute a <a
1116 href="#testcase">useful test case</a> or a workaround).</p>
1118 <p>If your bug seems new, then the best thing to do is report it via
1119 email to the <a
1120 href="http://cvs2svn.tigris.org/servlets/ProjectMailingListList">dev@cvs2svn.tigris.org</a>
1121 mailing list. Be sure to include the following information in your
1122 message:</p>
1124 <ol>
1126 <li><em>Exactly what version</em> of cvs2svn are you using? If you
1127 are not using an official release, please tell us what branch and
1128 revision number from the <a
1129 href="http://cvs2svn.tigris.org/svn/cvs2svn/">svn archive</a> you
1130 are using. If you have modified cvs2svn, please tell us exactly
1131 what you have changed.</li>
1133 <li>What platform are you using (Linux, BSD, Windows, etc.)? What
1134 python version (e.g., type <tt>python --version</tt>)?</li>
1136 <li>What is the <em>exact command line</em> that you used to start
1137 the conversion? If you used the <tt>--options</tt> option, please
1138 attach a copy of the options file that you used.</li>
1140 <li>What happened when you ran the program? Why do you think the
1141 behavior was wrong? Include transcripts and/or error output if
1142 available.</li>
1144 <li>If at all possible, include a test case repository that we can
1145 use to reproduce the problem. See <a href="#testcase">"How can I
1146 produce a useful test case?"</a> for more information. In most
1147 cases, if we cannot reproduce the problem, there is nothing we can
1148 do to help you.</li>
1150 </ol>
1153 <h3><a name="testcase" title="#testcase">How can I produce a useful
1154 test case?</a></h3>
1156 <p>If you need to <a href="#reportingbugs">report a bug</a>, it is
1157 extremely helpful if you can include a test repository with your bug
1158 report. In most cases, if we cannot reproduce the problem, there is
1159 nothing we can do to help you. This section describes ways to
1160 overcome the most common problems that people have in producing a
1161 useful test case. When you have a reasonable-sized test case (say
1162 under 1 MB--the smaller the better), you can just tar it up and attach
1163 it to the email in which you report the bug.</p>
1165 <h4>If the repository is too big and/or contains proprietary information</h4>
1167 <p>You don't want to send us your proprietary information, and we
1168 don't want to receive it either. Short of open-sourcing your
1169 software, here is a way to strip out most of the proprietary
1170 information and simultaneously reduce the size of the archive
1171 tremendously.</p>
1173 <p>The <tt>destroy_repository.py</tt> script tries to delete as much
1174 information as possible out of your repository while still preserving
1175 its basic structure (and therefore hopefully any cvs2svn bugs).
1176 Specifically, it tries to delete file descriptions, text content, all
1177 nontrivial log messages, and all author names. It also renames all
1178 files and directories to have generic names (e.g.,
1179 <tt>dir015/file053,v</tt>). (It does not affect the number and dates
1180 of revisions to the files.)</p>
1182 <ol>
1184 <li>This procedure will <b>destroy the repository</b> that it is
1185 applied to, so be sure to <b>make a backup copy of your
1186 repository and work with the backup!</b></li>
1188 <li>Make sure you have the <tt>destroy_repository.py</tt> script.
1189 If you don't already have it, you should <a
1190 href="http://cvs2svn.tigris.org/servlets/ProjectSource">download the
1191 source code</a> for cvs2svn (there is no need to install it). The
1192 script is located in the <tt>contrib</tt> subdirectory.</li>
1194 <li>Run <tt>destroy_repository.py</tt> by typing <pre>
1195 # You did make a backup, right?
1196 /path/to/config/destroy_repository.py /path/to/copy/of/repo
1197 </pre></li>
1199 <li>Verify that the "destroyed" archive does not include any
1200 information that you consider proprietary. Your data security is
1201 ultimately your responsibility, and we make no guarantees that the
1202 <tt>destroy_repository.py</tt> script works correctly. You can open
1203 the *,v files using a text editor to see what they contain.</li>
1205 <li>Try converting the "destroyed" repository using cvs2svn, and
1206 ensure that the bug still exists. Take a note of the exact cvs2svn
1207 command line that you used and include it along with a tarball of
1208 the "destroyed" repository with your bug report.</li>
1210 </ol>
1212 <p>If running <tt>destroy_repository.py</tt> with its default options
1213 causes the bug to go away, consider using
1214 <tt>destroy_repository.py</tt> command-line options to leave part of
1215 the repository information intact. Run <tt>destroy_repository.py
1216 --help</tt> for more information.</p>
1219 <h4>The repository is still too large</h4>
1221 <p>This step is a tiny bit more work, so if your repository is already
1222 small enough to send you can skip this step. But this step helps
1223 narrow down the problem (maybe even point you to a corrupt file in
1224 your repository!) so it is still recommended.</p>
1226 <p>The <tt>shrink_test_case.py</tt> script tries to delete as many
1227 files and directories from your repository as possible while
1228 preserving the cvs2svn bug. To use this command, you need to write a
1229 little test script that tries to convert your repository and checks
1230 whether the bug is still present. The script should exit successfully
1231 (e.g., "<tt>exit 0</tt>") if the bug is still <em>present</em>, and
1232 fail (e.g., "<tt>exit 1</tt>") if the bug has <em>disappeared</em>.
1233 The form of the test script depends on the bug that you saw, but it
1234 can be as simple as something like this:</p>
1236 <pre>
1237 #! /bin/sh
1239 cvs2svn --dry-run /path/to/copy/of/repo 2>&amp;1 | grep -q 'KeyError'
1240 </pre>
1242 <p>If the bug is more subtle, then the test script obviously needs to
1243 be more involved.</p>
1245 <p>Once the test script is ready, you can shrink your repository via
1246 the following steps:</p>
1248 <ol>
1250 <li>This procedure will <b>destroy the repository</b> that it is
1251 applied to, so be sure to <b>make a backup copy of your
1252 repository and work with the backup!</b></li>
1254 <li>Make sure you have the <tt>shrink_test_case.py</tt> script.
1255 If you don't already have it, you should <a
1256 href="http://cvs2svn.tigris.org/servlets/ProjectSource">download the
1257 source code</a> for cvs2svn (there is no need to install it). The
1258 script is located in the <tt>contrib</tt> subdirectory.</li>
1260 <li>Run <tt>shrink_test_case.py</tt> by typing <pre>
1261 # You did make a backup, right?
1262 /path/to/config/shrink_test_case.py /path/to/copy/of/repo testscript.sh
1263 </pre>, where <tt>testscript.sh</tt> is the name of the test script
1264 described above. This script will execute <tt>testscript.sh</tt>
1265 many times, each time using a subset of the original repository.</li>
1267 <li>If the shrunken repository only consists of one or two files,
1268 look inside the files with a text editor to see whether they are
1269 corrupted in any obvious way. (Many so-called cvs2svn "bugs" are
1270 actually the result of a corrupt CVS repository.)</li>
1272 <li>Try converting the "shrunk" repository using cvs2svn, to make
1273 sure that the original bug still exists. Take a note of the exact
1274 cvs2svn command line that you used, and include it along with a
1275 tarball of the "destroyed" repository with your bug report.</li>
1277 </ol>
1280 <h3><a name="commercialsupport" title="#commercialsupport">Does
1281 anybody offer commercial support for cvs2svn/cvs2git
1282 conversions?</a></h3>
1284 <p><b>Disclaimer:</b>These links in this section are provided as a
1285 service to cvs2svn/cvs2git users. Neither Tigris.org, CollabNet
1286 Inc., nor the cvs2svn team guarantee the correctness, validity or
1287 usefulness of these links. To add a link to this section, please
1288 submit it to the cvs2svn developers' mailing list.</p>
1290 <p>Following is a list of known sources for commercial support for
1291 cvs2svn/cvs2git conversions:</p>
1293 <ul>
1295 <li>Michael Haggerty, the maintainer of cvs2svn/cvs2git, offers
1296 individual help with conversions, including implementation of new
1297 cvs2svn/cvs2git features, on a consulting basis. Please contact
1298 Michael <a href="email:mhagger@alum.mit.edu">via email</a> for more
1299 information.</li>
1301 </ul>
1304 </div>
1305 </body>
1306 </html>