1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2006 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 """This module provides access to the CVS repository for cvs2svn."""
24 from common
import FatalError
28 CommandFailedException
32 """A CVS repository from which data can be extracted."""
34 def __init__(self
, cvs_repos_path
):
35 """CVS_REPOS_PATH is the top of the CVS repository (at least as
36 far as this run is concerned)."""
38 if not os
.path
.isdir(cvs_repos_path
):
39 raise FatalError("The specified CVS repository path '%s' is not an "
40 "existing directory." % cvs_repos_path
)
42 self
.cvs_repos_path
= os
.path
.normpath(cvs_repos_path
)
43 self
.cvs_prefix_re
= re
.compile(
44 r
'^' + re
.escape(self
.cvs_repos_path
)
45 + r
'(' + re
.escape(os
.sep
) + r
'|$)')
47 def get_cvs_path(self
, fname
):
48 """Return the path to FNAME relative to cvs_repos_path, with ',v' removed.
50 FNAME is a filesystem name that has to be within
51 self.cvs_repos_path. Return the filename relative to
52 self.cvs_repos_path, with ',v' striped off if present, and with
53 os.sep converted to '/'."""
55 (tail
, n
) = self
.cvs_prefix_re
.subn('', fname
, 1)
58 "get_cvs_path: '%s' is not a sub-path of '%s'"
59 % (fname
, self
.cvs_repos_path
,))
60 if tail
.endswith(',v'):
62 return tail
.replace(os
.sep
, '/')
64 def get_co_pipe(self
, c_rev
, suppress_keyword_substitution
=False):
65 """Return a command string, and a pipe from which the file
66 contents of C_REV can be read. C_REV is a CVSRevision. If
67 SUPPRESS_KEYWORD_SUBSTITUTION is True, then suppress the
68 substitution of RCS/CVS keywords in the output. Standard output
69 of the pipe returns the text of that CVS Revision.
71 The command string that is returned is provided for use in error
72 messages; it is not escaped in such a way that it could
73 necessarily be executed."""
75 raise NotImplementedError
78 class CVSRepositoryViaRCS(CVSRepository
):
79 """A CVSRepository accessed via RCS."""
81 def __init__(self
, cvs_repos_path
):
82 CVSRepository
.__init
__(self
, cvs_repos_path
)
84 check_command_runs([ 'co', '-V' ], 'co')
85 except CommandFailedException
, e
:
86 raise FatalError('%s\n'
87 'Please check that co is installed and in your PATH\n'
88 '(it is a part of the RCS software).' % (e
,))
90 def get_co_pipe(self
, c_rev
, suppress_keyword_substitution
=False):
91 pipe_cmd
= [ 'co', '-q', '-x,v', '-p' + c_rev
.rev
]
92 if suppress_keyword_substitution
:
93 pipe_cmd
.append('-kk')
94 pipe_cmd
.append(c_rev
.cvs_file
.filename
)
95 pipe
= SimplePopen(pipe_cmd
, True)
97 return ' '.join(pipe_cmd
), pipe
100 class CVSRepositoryViaCVS(CVSRepository
):
101 """A CVSRepository accessed via CVS."""
103 def __init__(self
, cvs_repos_path
):
104 CVSRepository
.__init
__(self
, cvs_repos_path
)
105 # Ascend above the specified root if necessary, to find the
106 # cvs_repository_root (a directory containing a CVSROOT directory)
107 # and the cvs_module (the path of the conversion root within the
108 # cvs repository) NB: cvs_module must be seperated by '/' *not* by
110 def is_cvs_repository_root(path
):
111 return os
.path
.isdir(os
.path
.join(path
, 'CVSROOT'))
113 self
.cvs_repository_root
= os
.path
.abspath(self
.cvs_repos_path
)
115 while not is_cvs_repository_root(self
.cvs_repository_root
):
116 # Step up one directory:
117 prev_cvs_repository_root
= self
.cvs_repository_root
118 self
.cvs_repository_root
, module_component
= \
119 os
.path
.split(self
.cvs_repository_root
)
120 if self
.cvs_repository_root
== prev_cvs_repository_root
:
121 # Hit the root (of the drive, on Windows) without finding a
124 "the path '%s' is not a CVS repository, nor a path "
125 "within a CVS repository. A CVS repository contains "
126 "a CVSROOT directory within its root directory."
127 % (self
.cvs_repos_path
,))
129 self
.cvs_module
= module_component
+ "/" + self
.cvs_module
131 os
.environ
['CVSROOT'] = self
.cvs_repository_root
133 def cvs_ok(global_arguments
):
135 [ 'cvs' ] + global_arguments
+ [ '--version' ], 'cvs')
137 self
.global_arguments
= [ "-q", "-R" ]
139 cvs_ok(self
.global_arguments
)
140 except CommandFailedException
, e
:
141 self
.global_arguments
= [ "-q" ]
143 cvs_ok(self
.global_arguments
)
144 except CommandFailedException
, e
:
147 'Please check that cvs is installed and in your PATH.' % (e
,))
149 def get_co_pipe(self
, c_rev
, suppress_keyword_substitution
=False):
150 pipe_cmd
= [ 'cvs' ] + self
.global_arguments
+ \
151 [ 'co', '-r' + c_rev
.rev
, '-p' ]
152 if suppress_keyword_substitution
:
153 pipe_cmd
.append('-kk')
154 pipe_cmd
.append(self
.cvs_module
+ c_rev
.cvs_path
)
155 pipe
= SimplePopen(pipe_cmd
, True)
157 return ' '.join(pipe_cmd
), pipe