Fix some stray tabs.
[cvs2svn.git] / cvs2svn_lib / svn_output_option.py
blobb03e38652ed9223beed22b3be390845fbacbb2b9
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2009 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """Classes for outputting the converted repository to SVN."""
20 import os
22 from cvs2svn_lib import config
23 from cvs2svn_lib.common import InternalError
24 from cvs2svn_lib.common import FatalError
25 from cvs2svn_lib.common import FatalException
26 from cvs2svn_lib.common import error_prefix
27 from cvs2svn_lib.common import format_date
28 from cvs2svn_lib.common import PathsNotDisjointException
29 from cvs2svn_lib.common import verify_paths_disjoint
30 from cvs2svn_lib.log import Log
31 from cvs2svn_lib.context import Ctx
32 from cvs2svn_lib.artifact_manager import artifact_manager
33 from cvs2svn_lib.process import CommandFailedException
34 from cvs2svn_lib.process import check_command_runs
35 from cvs2svn_lib.process import call_command
36 from cvs2svn_lib.cvs_file import CVSDirectory
37 from cvs2svn_lib.symbol import Trunk
38 from cvs2svn_lib.symbol import LineOfDevelopment
39 from cvs2svn_lib.cvs_item import CVSRevisionAdd
40 from cvs2svn_lib.cvs_item import CVSRevisionChange
41 from cvs2svn_lib.cvs_item import CVSRevisionDelete
42 from cvs2svn_lib.cvs_item import CVSRevisionNoop
43 from cvs2svn_lib.repository_mirror import RepositoryMirror
44 from cvs2svn_lib.repository_mirror import PathExistsError
45 from cvs2svn_lib.svn_commit_item import SVNCommitItem
46 from cvs2svn_lib.openings_closings import SymbolingsReader
47 from cvs2svn_lib.fill_source import get_source_set
48 from cvs2svn_lib.stdout_delegate import StdoutDelegate
49 from cvs2svn_lib.dumpfile_delegate import DumpfileDelegate
50 from cvs2svn_lib.repository_delegate import RepositoryDelegate
51 from cvs2svn_lib.output_option import OutputOption
54 class SVNOutputOption(OutputOption):
55 """An OutputOption appropriate for output to Subversion."""
57 class ParentMissingError(Exception):
58 """The parent of a path is missing.
60 Exception raised if an attempt is made to add a path to the
61 repository mirror but the parent's path doesn't exist in the
62 youngest revision of the repository."""
64 pass
66 class ExpectedDirectoryError(Exception):
67 """A file was found where a directory was expected."""
69 pass
71 def __init__(self, author_transforms=None):
72 self._mirror = RepositoryMirror()
74 def to_utf8(s):
75 if isinstance(s, unicode):
76 return s.encode('utf8')
77 else:
78 return s
80 self.author_transforms = {}
81 if author_transforms is not None:
82 for (cvsauthor, name) in author_transforms.iteritems():
83 cvsauthor = to_utf8(cvsauthor)
84 name = to_utf8(name)
85 self.author_transforms[cvsauthor] = name
87 def register_artifacts(self, which_pass):
88 # These artifacts are needed for SymbolingsReader:
89 artifact_manager.register_temp_file_needed(
90 config.SYMBOL_OPENINGS_CLOSINGS_SORTED, which_pass
92 artifact_manager.register_temp_file_needed(
93 config.SYMBOL_OFFSETS_DB, which_pass
96 self._mirror.register_artifacts(which_pass)
97 Ctx().revision_reader.register_artifacts(which_pass)
99 def check_symbols(self, symbol_map):
100 """Check that the paths of all included LODs are set and disjoint."""
102 error_found = False
104 # Check that all included LODs have their base paths set, and
105 # collect the paths into a list:
106 paths = []
107 for lod in symbol_map.itervalues():
108 if isinstance(lod, LineOfDevelopment):
109 if lod.base_path is None:
110 Log().error('%s: No path was set for %r\n' % (error_prefix, lod,))
111 error_found = True
112 else:
113 paths.append(lod.base_path)
115 # Check that the SVN paths of all LODS are disjoint:
116 try:
117 verify_paths_disjoint(*paths)
118 except PathsNotDisjointException, e:
119 Log().error(str(e))
120 error_found = True
122 if error_found:
123 raise FatalException(
124 'Please fix the above errors and restart CollateSymbolsPass'
127 def setup(self, svn_rev_count):
128 self._symbolings_reader = SymbolingsReader()
129 self._mirror.open()
130 self._delegates = []
131 Ctx().revision_reader.start()
132 self.add_delegate(StdoutDelegate(svn_rev_count))
134 def _get_author(self, svn_commit):
135 author = svn_commit.get_author()
136 name = self.author_transforms.get(author, author)
137 return name
139 def _get_revprops(self, svn_commit):
140 """Return the Subversion revprops for this SVNCommit."""
142 return {
143 'svn:author' : self._get_author(svn_commit),
144 'svn:log' : svn_commit.get_log_msg(),
145 'svn:date' : format_date(svn_commit.date),
148 def start_commit(self, revnum, revprops):
149 """Start a new commit."""
151 self._mirror.start_commit(revnum)
152 self._invoke_delegates('start_commit', revnum, revprops)
154 def end_commit(self):
155 """Called at the end of each commit.
157 This method copies the newly created nodes to the on-disk nodes
158 db."""
160 self._mirror.end_commit()
161 self._invoke_delegates('end_commit')
163 def delete_lod(self, lod):
164 """Delete the main path for LOD from the tree.
166 The path must currently exist. Silently refuse to delete trunk
167 paths."""
169 if isinstance(lod, Trunk):
170 # Never delete a Trunk path.
171 return
173 self._mirror.get_current_lod_directory(lod).delete()
174 self._invoke_delegates('delete_lod', lod)
176 def delete_path(self, cvs_path, lod, should_prune=False):
177 """Delete CVS_PATH from LOD."""
179 if cvs_path.parent_directory is None:
180 self.delete_lod(lod)
181 return
183 parent_node = self._mirror.get_current_path(
184 cvs_path.parent_directory, lod
186 del parent_node[cvs_path]
187 self._invoke_delegates('delete_path', lod, cvs_path)
189 if should_prune:
190 while parent_node is not None and len(parent_node) == 0:
191 # A drawback of this code is that we issue a delete for each
192 # path and not just a single delete for the topmost directory
193 # pruned.
194 node = parent_node
195 cvs_path = node.cvs_path
196 if cvs_path.parent_directory is None:
197 parent_node = None
198 self.delete_lod(lod)
199 else:
200 parent_node = node.parent_mirror_dir
201 node.delete()
202 self._invoke_delegates('delete_path', lod, cvs_path)
204 def initialize_project(self, project):
205 """Create the basic structure for PROJECT."""
207 self._invoke_delegates('initialize_project', project)
209 # Don't invoke delegates.
210 self._mirror.add_lod(project.get_trunk())
212 def change_path(self, cvs_rev):
213 """Register a change in self._youngest for the CVS_REV's svn_path."""
215 # We do not have to update the nodes because our mirror is only
216 # concerned with the presence or absence of paths, and a file
217 # content change does not cause any path changes.
218 self._invoke_delegates('change_path', SVNCommitItem(cvs_rev, False))
220 def _mkdir_p(self, cvs_directory, lod):
221 """Make sure that CVS_DIRECTORY exists in LOD.
223 If not, create it, calling delegates. Return the node for
224 CVS_DIRECTORY."""
226 try:
227 node = self._mirror.get_current_lod_directory(lod)
228 except KeyError:
229 node = self._mirror.add_lod(lod)
230 self._invoke_delegates('initialize_lod', lod)
232 for sub_path in cvs_directory.get_ancestry()[1:]:
233 try:
234 node = node[sub_path]
235 except KeyError:
236 node = node.mkdir(sub_path)
237 self._invoke_delegates('mkdir', lod, sub_path)
238 if node is None:
239 raise self.ExpectedDirectoryError(
240 'File found at \'%s\' where directory was expected.' % (sub_path,)
243 return node
245 def add_path(self, cvs_rev):
246 """Add the CVS_REV's svn_path to the repository mirror.
248 Create any missing intermediate paths."""
250 cvs_file = cvs_rev.cvs_file
251 parent_path = cvs_file.parent_directory
252 lod = cvs_rev.lod
253 parent_node = self._mkdir_p(parent_path, lod)
254 parent_node.add_file(cvs_file)
255 self._invoke_delegates('add_path', SVNCommitItem(cvs_rev, True))
257 def copy_lod(self, src_lod, dest_lod, src_revnum):
258 """Copy all of SRC_LOD at SRC_REVNUM to DST_LOD.
260 In the youngest revision of the repository, the destination LOD
261 *must not* already exist.
263 Return the new node at DEST_LOD. Note that this node is not
264 necessarily writable, though its parent node necessarily is."""
266 node = self._mirror.copy_lod(src_lod, dest_lod, src_revnum)
267 self._invoke_delegates('copy_lod', src_lod, dest_lod, src_revnum)
268 return node
270 def copy_path(
271 self, cvs_path, src_lod, dest_lod, src_revnum, create_parent=False
273 """Copy CVS_PATH from SRC_LOD at SRC_REVNUM to DST_LOD.
275 In the youngest revision of the repository, the destination's
276 parent *must* exist unless CREATE_PARENT is specified. But the
277 destination itself *must not* exist.
279 Return the new node at (CVS_PATH, DEST_LOD), as a
280 CurrentMirrorDirectory."""
282 if cvs_path.parent_directory is None:
283 return self.copy_lod(src_lod, dest_lod, src_revnum)
285 # Get the node of our source, or None if it is a file:
286 src_node = self._mirror.get_old_path(cvs_path, src_lod, src_revnum)
288 # Get the parent path of the destination:
289 if create_parent:
290 dest_parent_node = self._mkdir_p(cvs_path.parent_directory, dest_lod)
291 else:
292 try:
293 dest_parent_node = self._mirror.get_current_path(
294 cvs_path.parent_directory, dest_lod
296 except KeyError:
297 raise self.ParentMissingError(
298 'Attempt to add path \'%s\' to repository mirror, '
299 'but its parent directory doesn\'t exist in the mirror.'
300 % (dest_lod.get_path(cvs_path.cvs_path),)
303 if cvs_path in dest_parent_node:
304 raise PathExistsError(
305 'Attempt to add path \'%s\' to repository mirror '
306 'when it already exists in the mirror.'
307 % (dest_lod.get_path(cvs_path.cvs_path),)
310 dest_parent_node[cvs_path] = src_node
311 self._invoke_delegates(
312 'copy_path',
313 cvs_path, src_lod, dest_lod, src_revnum
316 return dest_parent_node[cvs_path]
318 def fill_symbol(self, svn_symbol_commit, fill_source):
319 """Perform all copies for the CVSSymbols in SVN_SYMBOL_COMMIT.
321 The symbolic name is guaranteed to exist in the Subversion
322 repository by the end of this call, even if there are no paths
323 under it."""
325 symbol = svn_symbol_commit.symbol
327 try:
328 dest_node = self._mirror.get_current_lod_directory(symbol)
329 except KeyError:
330 self._fill_directory(symbol, None, fill_source, None)
331 else:
332 self._fill_directory(symbol, dest_node, fill_source, None)
334 def _fill_directory(self, symbol, dest_node, fill_source, parent_source):
335 """Fill the tag or branch SYMBOL at the path indicated by FILL_SOURCE.
337 Use items from FILL_SOURCE, and recurse into the child items.
339 Fill SYMBOL starting at the path FILL_SOURCE.cvs_path. DEST_NODE
340 is the node of this destination path, or None if the destination
341 does not yet exist. All directories above this path have already
342 been filled. FILL_SOURCE is a FillSource instance describing the
343 items within a subtree of the repository that still need to be
344 copied to the destination.
346 PARENT_SOURCE is the SVNRevisionRange that was used to copy the
347 parent directory, if it was copied in this commit. We prefer to
348 copy from the same source as was used for the parent, since it
349 typically requires less touching-up. If PARENT_SOURCE is None,
350 then the parent directory was not copied in this commit, so no
351 revision is preferable to any other."""
353 copy_source = fill_source.compute_best_source(parent_source)
355 # Figure out if we shall copy to this destination and delete any
356 # destination path that is in the way.
357 if dest_node is None:
358 # The destination does not exist at all, so it definitely has to
359 # be copied:
360 dest_node = self.copy_path(
361 fill_source.cvs_path, copy_source.source_lod,
362 symbol, copy_source.opening_revnum
364 elif (parent_source is not None) and (
365 copy_source.source_lod != parent_source.source_lod
366 or copy_source.opening_revnum != parent_source.opening_revnum
368 # The parent path was copied from a different source than we
369 # need to use, so we have to delete the version that was copied
370 # with the parent then re-copy from the correct source:
371 self.delete_path(fill_source.cvs_path, symbol)
372 dest_node = self.copy_path(
373 fill_source.cvs_path, copy_source.source_lod,
374 symbol, copy_source.opening_revnum
376 else:
377 copy_source = parent_source
379 # The map {CVSPath : FillSource} of entries within this directory
380 # that need filling:
381 src_entries = fill_source.get_subsource_map()
383 if copy_source is not None:
384 self._prune_extra_entries(
385 fill_source.cvs_path, symbol, dest_node, src_entries
388 return self._cleanup_filled_directory(
389 symbol, dest_node, src_entries, copy_source
392 def _cleanup_filled_directory(
393 self, symbol, dest_node, src_entries, copy_source
395 """The directory at DEST_NODE has been filled and pruned; recurse.
397 Recurse into the SRC_ENTRIES, in alphabetical order. If DEST_NODE
398 was copied in this revision, COPY_SOURCE should indicate where it
399 was copied from; otherwise, COPY_SOURCE should be None."""
401 cvs_paths = src_entries.keys()
402 cvs_paths.sort()
403 for cvs_path in cvs_paths:
404 if isinstance(cvs_path, CVSDirectory):
405 # Path is a CVSDirectory:
406 try:
407 dest_subnode = dest_node[cvs_path]
408 except KeyError:
409 # Path doesn't exist yet; it has to be created:
410 dest_node = self._fill_directory(
411 symbol, None, src_entries[cvs_path], None
412 ).parent_mirror_dir
413 else:
414 # Path already exists, but might have to be cleaned up:
415 dest_node = self._fill_directory(
416 symbol, dest_subnode, src_entries[cvs_path], copy_source
417 ).parent_mirror_dir
418 else:
419 # Path is a CVSFile:
420 self._fill_file(
421 symbol, cvs_path in dest_node, src_entries[cvs_path], copy_source
423 # Reread dest_node since the call to _fill_file() might have
424 # made it writable:
425 dest_node = self._mirror.get_current_path(
426 dest_node.cvs_path, dest_node.lod
429 return dest_node
431 def _fill_file(self, symbol, dest_existed, fill_source, parent_source):
432 """Fill the tag or branch SYMBOL at the path indicated by FILL_SOURCE.
434 Use items from FILL_SOURCE.
436 Fill SYMBOL at path FILL_SOURCE.cvs_path. DEST_NODE is the node
437 of this destination path, or None if the destination does not yet
438 exist. All directories above this path have already been filled
439 as needed. FILL_SOURCE is a FillSource instance describing the
440 item that needs to be copied to the destination.
442 PARENT_SOURCE is the source from which the parent directory was
443 copied, or None if the parent directory was not copied during this
444 commit. We prefer to copy from PARENT_SOURCE, since it typically
445 requires less touching-up. If PARENT_SOURCE is None, then the
446 parent directory was not copied in this commit, so no revision is
447 preferable to any other."""
449 copy_source = fill_source.compute_best_source(parent_source)
451 # Figure out if we shall copy to this destination and delete any
452 # destination path that is in the way.
453 if not dest_existed:
454 # The destination does not exist at all, so it definitely has to
455 # be copied:
456 self.copy_path(
457 fill_source.cvs_path, copy_source.source_lod,
458 symbol, copy_source.opening_revnum
460 elif (parent_source is not None) and (
461 copy_source.source_lod != parent_source.source_lod
462 or copy_source.opening_revnum != parent_source.opening_revnum
464 # The parent path was copied from a different source than we
465 # need to use, so we have to delete the version that was copied
466 # with the parent and then re-copy from the correct source:
467 self.delete_path(fill_source.cvs_path, symbol)
468 self.copy_path(
469 fill_source.cvs_path, copy_source.source_lod,
470 symbol, copy_source.opening_revnum
473 def _prune_extra_entries(
474 self, dest_cvs_path, symbol, dest_node, src_entries
476 """Delete any entries in DEST_NODE that are not in SRC_ENTRIES."""
478 delete_list = [
479 cvs_path
480 for cvs_path in dest_node
481 if cvs_path not in src_entries
484 # Sort the delete list so that the output is in a consistent
485 # order:
486 delete_list.sort()
487 for cvs_path in delete_list:
488 del dest_node[cvs_path]
489 self._invoke_delegates('delete_path', symbol, cvs_path)
491 def add_delegate(self, delegate):
492 """Adds DELEGATE to self._delegates.
494 For every delegate you add, whenever a repository action method is
495 performed, delegate's corresponding repository action method is
496 called. Multiple delegates will be called in the order that they
497 are added. See SVNRepositoryDelegate for more information."""
499 self._delegates.append(delegate)
501 def _invoke_delegates(self, method, *args):
502 """Invoke a method on each delegate.
504 Iterate through each of our delegates, in the order that they were
505 added, and call the delegate's method named METHOD with the
506 arguments in ARGS."""
508 for delegate in self._delegates:
509 getattr(delegate, method)(*args)
511 def process_initial_project_commit(self, svn_commit):
512 self.start_commit(svn_commit.revnum, self._get_revprops(svn_commit))
514 for project in svn_commit.projects:
515 self.initialize_project(project)
517 self.end_commit()
519 def process_primary_commit(self, svn_commit):
520 self.start_commit(svn_commit.revnum, self._get_revprops(svn_commit))
522 # This actually commits CVSRevisions
523 if len(svn_commit.cvs_revs) > 1:
524 plural = "s"
525 else:
526 plural = ""
527 Log().verbose("Committing %d CVSRevision%s"
528 % (len(svn_commit.cvs_revs), plural))
529 for cvs_rev in svn_commit.cvs_revs:
530 if isinstance(cvs_rev, CVSRevisionNoop):
531 pass
533 elif isinstance(cvs_rev, CVSRevisionDelete):
534 self.delete_path(cvs_rev.cvs_file, cvs_rev.lod, Ctx().prune)
536 elif isinstance(cvs_rev, CVSRevisionAdd):
537 self.add_path(cvs_rev)
539 elif isinstance(cvs_rev, CVSRevisionChange):
540 self.change_path(cvs_rev)
542 self.end_commit()
544 def process_post_commit(self, svn_commit):
545 self.start_commit(svn_commit.revnum, self._get_revprops(svn_commit))
547 Log().verbose(
548 'Synchronizing default branch motivated by %d'
549 % (svn_commit.motivating_revnum,)
552 for cvs_rev in svn_commit.cvs_revs:
553 trunk = cvs_rev.cvs_file.project.get_trunk()
554 if isinstance(cvs_rev, CVSRevisionAdd):
555 # Copy from branch to trunk:
556 self.copy_path(
557 cvs_rev.cvs_file, cvs_rev.lod, trunk,
558 svn_commit.motivating_revnum, True
560 elif isinstance(cvs_rev, CVSRevisionChange):
561 # Delete old version of the path on trunk...
562 self.delete_path(cvs_rev.cvs_file, trunk)
563 # ...and copy the new version over from branch:
564 self.copy_path(
565 cvs_rev.cvs_file, cvs_rev.lod, trunk,
566 svn_commit.motivating_revnum, True
568 elif isinstance(cvs_rev, CVSRevisionDelete):
569 # Delete trunk path:
570 self.delete_path(cvs_rev.cvs_file, trunk)
571 elif isinstance(cvs_rev, CVSRevisionNoop):
572 # Do nothing
573 pass
574 else:
575 raise InternalError('Unexpected CVSRevision type: %s' % (cvs_rev,))
577 self.end_commit()
579 def process_branch_commit(self, svn_commit):
580 self.start_commit(svn_commit.revnum, self._get_revprops(svn_commit))
581 Log().verbose('Filling branch:', svn_commit.symbol.name)
583 # Get the set of sources for the symbolic name:
584 source_set = get_source_set(
585 svn_commit.symbol,
586 self._symbolings_reader.get_range_map(svn_commit),
589 self.fill_symbol(svn_commit, source_set)
591 self.end_commit()
593 def process_tag_commit(self, svn_commit):
594 self.start_commit(svn_commit.revnum, self._get_revprops(svn_commit))
595 Log().verbose('Filling tag:', svn_commit.symbol.name)
597 # Get the set of sources for the symbolic name:
598 source_set = get_source_set(
599 svn_commit.symbol,
600 self._symbolings_reader.get_range_map(svn_commit),
603 self.fill_symbol(svn_commit, source_set)
605 self.end_commit()
607 def cleanup(self):
608 self._invoke_delegates('finish')
609 self._mirror.close()
610 self._mirror = None
611 Ctx().revision_reader.finish()
612 self._symbolings_reader.close()
613 del self._symbolings_reader
616 class DumpfileOutputOption(SVNOutputOption):
617 """Output the result of the conversion into a dumpfile."""
619 def __init__(self, dumpfile_path, author_transforms=None):
620 SVNOutputOption.__init__(self, author_transforms)
621 self.dumpfile_path = dumpfile_path
623 def check(self):
624 pass
626 def setup(self, svn_rev_count):
627 Log().quiet("Starting Subversion Dumpfile.")
628 SVNOutputOption.setup(self, svn_rev_count)
629 if not Ctx().dry_run:
630 self.add_delegate(
631 DumpfileDelegate(Ctx().revision_reader, self.dumpfile_path)
635 class RepositoryOutputOption(SVNOutputOption):
636 """Output the result of the conversion into an SVN repository."""
638 def __init__(self, target, author_transforms=None):
639 SVNOutputOption.__init__(self, author_transforms)
640 self.target = target
642 def check(self):
643 if not Ctx().dry_run:
644 # Verify that svnadmin can be executed. The 'help' subcommand
645 # should be harmless.
646 try:
647 check_command_runs([Ctx().svnadmin_executable, 'help'], 'svnadmin')
648 except CommandFailedException, e:
649 raise FatalError(
650 '%s\n'
651 'svnadmin could not be executed. Please ensure that it is\n'
652 'installed and/or use the --svnadmin option.' % (e,))
654 def setup(self, svn_rev_count):
655 Log().quiet("Starting Subversion Repository.")
656 SVNOutputOption.setup(self, svn_rev_count)
657 if not Ctx().dry_run:
658 self.add_delegate(
659 RepositoryDelegate(Ctx().revision_reader, self.target)
663 class NewRepositoryOutputOption(RepositoryOutputOption):
664 """Output the result of the conversion into a new SVN repository."""
666 def __init__(
667 self, target, fs_type=None, bdb_txn_nosync=None, author_transforms=None, create_options=[]
669 RepositoryOutputOption.__init__(self, target, author_transforms)
670 self.bdb_txn_nosync = bdb_txn_nosync
672 # Determine the options to be passed to "svnadmin create":
673 if not fs_type:
674 # User didn't say what kind repository (bdb, fsfs, etc). We
675 # still pass --bdb-txn-nosync. It's a no-op if the default
676 # repository type doesn't support it, but we definitely want it
677 # if BDB is the default.
678 self.create_options = ['--bdb-txn-nosync']
679 elif fs_type == 'bdb':
680 # User explicitly specified bdb.
682 # Since this is a BDB repository, pass --bdb-txn-nosync, because
683 # it gives us a 4-5x speed boost (if cvs2svn is creating the
684 # repository, cvs2svn should be the only program accessing the
685 # svn repository until cvs2svn is done). But we'll turn no-sync
686 # off in self.finish(), unless instructed otherwise.
687 self.create_options = ['--fs-type=bdb', '--bdb-txn-nosync']
688 else:
689 # User specified something other than bdb.
690 self.create_options = ['--fs-type=%s' % fs_type]
692 # Now append the user's explicitly-set create options:
693 self.create_options += create_options
695 def check(self):
696 RepositoryOutputOption.check(self)
697 if not Ctx().dry_run and os.path.exists(self.target):
698 raise FatalError("the svn-repos-path '%s' exists.\n"
699 "Remove it, or pass '--existing-svnrepos'."
700 % self.target)
702 def setup(self, svn_rev_count):
703 Log().normal("Creating new repository '%s'" % (self.target))
704 if Ctx().dry_run:
705 # Do not actually create repository:
706 pass
707 else:
708 call_command([
709 Ctx().svnadmin_executable, 'create',
710 ] + self.create_options + [
711 self.target
714 RepositoryOutputOption.setup(self, svn_rev_count)
716 def cleanup(self):
717 RepositoryOutputOption.cleanup(self)
719 # If this is a BDB repository, and we created the repository, and
720 # --bdb-no-sync wasn't passed, then comment out the DB_TXN_NOSYNC
721 # line in the DB_CONFIG file, because txn syncing should be on by
722 # default in BDB repositories.
724 # We determine if this is a BDB repository by looking for the
725 # DB_CONFIG file, which doesn't exist in FSFS, rather than by
726 # checking self.fs_type. That way this code will Do The Right
727 # Thing in all circumstances.
728 db_config = os.path.join(self.target, "db/DB_CONFIG")
729 if Ctx().dry_run:
730 # Do not change repository:
731 pass
732 elif not self.bdb_txn_nosync and os.path.exists(db_config):
733 no_sync = 'set_flags DB_TXN_NOSYNC\n'
735 contents = open(db_config, 'r').readlines()
736 index = contents.index(no_sync)
737 contents[index] = '# ' + no_sync
738 open(db_config, 'w').writelines(contents)
741 class ExistingRepositoryOutputOption(RepositoryOutputOption):
742 """Output the result of the conversion into an existing SVN repository."""
744 def __init__(self, target, author_transforms=None):
745 RepositoryOutputOption.__init__(self, target, author_transforms)
747 def check(self):
748 RepositoryOutputOption.check(self)
749 if not os.path.isdir(self.target):
750 raise FatalError("the svn-repos-path '%s' is not an "
751 "existing directory." % self.target)