##// END OF EJS Templates
configitems: register the 'convert.cvsps.cache' config...
Boris Feld -
r34152:88601009 default
parent child Browse files
Show More
@@ -1,509 +1,516 b''
1 # convert.py Foreign SCM converter
1 # convert.py Foreign SCM converter
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''import revisions from foreign VCS repositories into Mercurial'''
8 '''import revisions from foreign VCS repositories into Mercurial'''
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13 from mercurial import (
13 from mercurial import (
14 registrar,
14 registrar,
15 )
15 )
16
16
17 from . import (
17 from . import (
18 convcmd,
18 convcmd,
19 cvsps,
19 cvsps,
20 subversion,
20 subversion,
21 )
21 )
22
22
23 cmdtable = {}
23 cmdtable = {}
24 command = registrar.command(cmdtable)
24 command = registrar.command(cmdtable)
25 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
25 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
26 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
26 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
27 # be specifying the version(s) of Mercurial they are tested with, or
27 # be specifying the version(s) of Mercurial they are tested with, or
28 # leave the attribute unspecified.
28 # leave the attribute unspecified.
29 testedwith = 'ships-with-hg-core'
29 testedwith = 'ships-with-hg-core'
30
30
31 configtable = {}
32 configitem = registrar.configitem(configtable)
33
34 configitem('convert', 'cvsps.cache',
35 default=True,
36 )
37
31 # Commands definition was moved elsewhere to ease demandload job.
38 # Commands definition was moved elsewhere to ease demandload job.
32
39
33 @command('convert',
40 @command('convert',
34 [('', 'authors', '',
41 [('', 'authors', '',
35 _('username mapping filename (DEPRECATED) (use --authormap instead)'),
42 _('username mapping filename (DEPRECATED) (use --authormap instead)'),
36 _('FILE')),
43 _('FILE')),
37 ('s', 'source-type', '', _('source repository type'), _('TYPE')),
44 ('s', 'source-type', '', _('source repository type'), _('TYPE')),
38 ('d', 'dest-type', '', _('destination repository type'), _('TYPE')),
45 ('d', 'dest-type', '', _('destination repository type'), _('TYPE')),
39 ('r', 'rev', [], _('import up to source revision REV'), _('REV')),
46 ('r', 'rev', [], _('import up to source revision REV'), _('REV')),
40 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
47 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
41 ('', 'filemap', '', _('remap file names using contents of file'),
48 ('', 'filemap', '', _('remap file names using contents of file'),
42 _('FILE')),
49 _('FILE')),
43 ('', 'full', None,
50 ('', 'full', None,
44 _('apply filemap changes by converting all files again')),
51 _('apply filemap changes by converting all files again')),
45 ('', 'splicemap', '', _('splice synthesized history into place'),
52 ('', 'splicemap', '', _('splice synthesized history into place'),
46 _('FILE')),
53 _('FILE')),
47 ('', 'branchmap', '', _('change branch names while converting'),
54 ('', 'branchmap', '', _('change branch names while converting'),
48 _('FILE')),
55 _('FILE')),
49 ('', 'branchsort', None, _('try to sort changesets by branches')),
56 ('', 'branchsort', None, _('try to sort changesets by branches')),
50 ('', 'datesort', None, _('try to sort changesets by date')),
57 ('', 'datesort', None, _('try to sort changesets by date')),
51 ('', 'sourcesort', None, _('preserve source changesets order')),
58 ('', 'sourcesort', None, _('preserve source changesets order')),
52 ('', 'closesort', None, _('try to reorder closed revisions'))],
59 ('', 'closesort', None, _('try to reorder closed revisions'))],
53 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]'),
60 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]'),
54 norepo=True)
61 norepo=True)
55 def convert(ui, src, dest=None, revmapfile=None, **opts):
62 def convert(ui, src, dest=None, revmapfile=None, **opts):
56 """convert a foreign SCM repository to a Mercurial one.
63 """convert a foreign SCM repository to a Mercurial one.
57
64
58 Accepted source formats [identifiers]:
65 Accepted source formats [identifiers]:
59
66
60 - Mercurial [hg]
67 - Mercurial [hg]
61 - CVS [cvs]
68 - CVS [cvs]
62 - Darcs [darcs]
69 - Darcs [darcs]
63 - git [git]
70 - git [git]
64 - Subversion [svn]
71 - Subversion [svn]
65 - Monotone [mtn]
72 - Monotone [mtn]
66 - GNU Arch [gnuarch]
73 - GNU Arch [gnuarch]
67 - Bazaar [bzr]
74 - Bazaar [bzr]
68 - Perforce [p4]
75 - Perforce [p4]
69
76
70 Accepted destination formats [identifiers]:
77 Accepted destination formats [identifiers]:
71
78
72 - Mercurial [hg]
79 - Mercurial [hg]
73 - Subversion [svn] (history on branches is not preserved)
80 - Subversion [svn] (history on branches is not preserved)
74
81
75 If no revision is given, all revisions will be converted.
82 If no revision is given, all revisions will be converted.
76 Otherwise, convert will only import up to the named revision
83 Otherwise, convert will only import up to the named revision
77 (given in a format understood by the source).
84 (given in a format understood by the source).
78
85
79 If no destination directory name is specified, it defaults to the
86 If no destination directory name is specified, it defaults to the
80 basename of the source with ``-hg`` appended. If the destination
87 basename of the source with ``-hg`` appended. If the destination
81 repository doesn't exist, it will be created.
88 repository doesn't exist, it will be created.
82
89
83 By default, all sources except Mercurial will use --branchsort.
90 By default, all sources except Mercurial will use --branchsort.
84 Mercurial uses --sourcesort to preserve original revision numbers
91 Mercurial uses --sourcesort to preserve original revision numbers
85 order. Sort modes have the following effects:
92 order. Sort modes have the following effects:
86
93
87 --branchsort convert from parent to child revision when possible,
94 --branchsort convert from parent to child revision when possible,
88 which means branches are usually converted one after
95 which means branches are usually converted one after
89 the other. It generates more compact repositories.
96 the other. It generates more compact repositories.
90
97
91 --datesort sort revisions by date. Converted repositories have
98 --datesort sort revisions by date. Converted repositories have
92 good-looking changelogs but are often an order of
99 good-looking changelogs but are often an order of
93 magnitude larger than the same ones generated by
100 magnitude larger than the same ones generated by
94 --branchsort.
101 --branchsort.
95
102
96 --sourcesort try to preserve source revisions order, only
103 --sourcesort try to preserve source revisions order, only
97 supported by Mercurial sources.
104 supported by Mercurial sources.
98
105
99 --closesort try to move closed revisions as close as possible
106 --closesort try to move closed revisions as close as possible
100 to parent branches, only supported by Mercurial
107 to parent branches, only supported by Mercurial
101 sources.
108 sources.
102
109
103 If ``REVMAP`` isn't given, it will be put in a default location
110 If ``REVMAP`` isn't given, it will be put in a default location
104 (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
111 (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
105 text file that maps each source commit ID to the destination ID
112 text file that maps each source commit ID to the destination ID
106 for that revision, like so::
113 for that revision, like so::
107
114
108 <source ID> <destination ID>
115 <source ID> <destination ID>
109
116
110 If the file doesn't exist, it's automatically created. It's
117 If the file doesn't exist, it's automatically created. It's
111 updated on each commit copied, so :hg:`convert` can be interrupted
118 updated on each commit copied, so :hg:`convert` can be interrupted
112 and can be run repeatedly to copy new commits.
119 and can be run repeatedly to copy new commits.
113
120
114 The authormap is a simple text file that maps each source commit
121 The authormap is a simple text file that maps each source commit
115 author to a destination commit author. It is handy for source SCMs
122 author to a destination commit author. It is handy for source SCMs
116 that use unix logins to identify authors (e.g.: CVS). One line per
123 that use unix logins to identify authors (e.g.: CVS). One line per
117 author mapping and the line format is::
124 author mapping and the line format is::
118
125
119 source author = destination author
126 source author = destination author
120
127
121 Empty lines and lines starting with a ``#`` are ignored.
128 Empty lines and lines starting with a ``#`` are ignored.
122
129
123 The filemap is a file that allows filtering and remapping of files
130 The filemap is a file that allows filtering and remapping of files
124 and directories. Each line can contain one of the following
131 and directories. Each line can contain one of the following
125 directives::
132 directives::
126
133
127 include path/to/file-or-dir
134 include path/to/file-or-dir
128
135
129 exclude path/to/file-or-dir
136 exclude path/to/file-or-dir
130
137
131 rename path/to/source path/to/destination
138 rename path/to/source path/to/destination
132
139
133 Comment lines start with ``#``. A specified path matches if it
140 Comment lines start with ``#``. A specified path matches if it
134 equals the full relative name of a file or one of its parent
141 equals the full relative name of a file or one of its parent
135 directories. The ``include`` or ``exclude`` directive with the
142 directories. The ``include`` or ``exclude`` directive with the
136 longest matching path applies, so line order does not matter.
143 longest matching path applies, so line order does not matter.
137
144
138 The ``include`` directive causes a file, or all files under a
145 The ``include`` directive causes a file, or all files under a
139 directory, to be included in the destination repository. The default
146 directory, to be included in the destination repository. The default
140 if there are no ``include`` statements is to include everything.
147 if there are no ``include`` statements is to include everything.
141 If there are any ``include`` statements, nothing else is included.
148 If there are any ``include`` statements, nothing else is included.
142 The ``exclude`` directive causes files or directories to
149 The ``exclude`` directive causes files or directories to
143 be omitted. The ``rename`` directive renames a file or directory if
150 be omitted. The ``rename`` directive renames a file or directory if
144 it is converted. To rename from a subdirectory into the root of
151 it is converted. To rename from a subdirectory into the root of
145 the repository, use ``.`` as the path to rename to.
152 the repository, use ``.`` as the path to rename to.
146
153
147 ``--full`` will make sure the converted changesets contain exactly
154 ``--full`` will make sure the converted changesets contain exactly
148 the right files with the right content. It will make a full
155 the right files with the right content. It will make a full
149 conversion of all files, not just the ones that have
156 conversion of all files, not just the ones that have
150 changed. Files that already are correct will not be changed. This
157 changed. Files that already are correct will not be changed. This
151 can be used to apply filemap changes when converting
158 can be used to apply filemap changes when converting
152 incrementally. This is currently only supported for Mercurial and
159 incrementally. This is currently only supported for Mercurial and
153 Subversion.
160 Subversion.
154
161
155 The splicemap is a file that allows insertion of synthetic
162 The splicemap is a file that allows insertion of synthetic
156 history, letting you specify the parents of a revision. This is
163 history, letting you specify the parents of a revision. This is
157 useful if you want to e.g. give a Subversion merge two parents, or
164 useful if you want to e.g. give a Subversion merge two parents, or
158 graft two disconnected series of history together. Each entry
165 graft two disconnected series of history together. Each entry
159 contains a key, followed by a space, followed by one or two
166 contains a key, followed by a space, followed by one or two
160 comma-separated values::
167 comma-separated values::
161
168
162 key parent1, parent2
169 key parent1, parent2
163
170
164 The key is the revision ID in the source
171 The key is the revision ID in the source
165 revision control system whose parents should be modified (same
172 revision control system whose parents should be modified (same
166 format as a key in .hg/shamap). The values are the revision IDs
173 format as a key in .hg/shamap). The values are the revision IDs
167 (in either the source or destination revision control system) that
174 (in either the source or destination revision control system) that
168 should be used as the new parents for that node. For example, if
175 should be used as the new parents for that node. For example, if
169 you have merged "release-1.0" into "trunk", then you should
176 you have merged "release-1.0" into "trunk", then you should
170 specify the revision on "trunk" as the first parent and the one on
177 specify the revision on "trunk" as the first parent and the one on
171 the "release-1.0" branch as the second.
178 the "release-1.0" branch as the second.
172
179
173 The branchmap is a file that allows you to rename a branch when it is
180 The branchmap is a file that allows you to rename a branch when it is
174 being brought in from whatever external repository. When used in
181 being brought in from whatever external repository. When used in
175 conjunction with a splicemap, it allows for a powerful combination
182 conjunction with a splicemap, it allows for a powerful combination
176 to help fix even the most badly mismanaged repositories and turn them
183 to help fix even the most badly mismanaged repositories and turn them
177 into nicely structured Mercurial repositories. The branchmap contains
184 into nicely structured Mercurial repositories. The branchmap contains
178 lines of the form::
185 lines of the form::
179
186
180 original_branch_name new_branch_name
187 original_branch_name new_branch_name
181
188
182 where "original_branch_name" is the name of the branch in the
189 where "original_branch_name" is the name of the branch in the
183 source repository, and "new_branch_name" is the name of the branch
190 source repository, and "new_branch_name" is the name of the branch
184 is the destination repository. No whitespace is allowed in the new
191 is the destination repository. No whitespace is allowed in the new
185 branch name. This can be used to (for instance) move code in one
192 branch name. This can be used to (for instance) move code in one
186 repository from "default" to a named branch.
193 repository from "default" to a named branch.
187
194
188 Mercurial Source
195 Mercurial Source
189 ################
196 ################
190
197
191 The Mercurial source recognizes the following configuration
198 The Mercurial source recognizes the following configuration
192 options, which you can set on the command line with ``--config``:
199 options, which you can set on the command line with ``--config``:
193
200
194 :convert.hg.ignoreerrors: ignore integrity errors when reading.
201 :convert.hg.ignoreerrors: ignore integrity errors when reading.
195 Use it to fix Mercurial repositories with missing revlogs, by
202 Use it to fix Mercurial repositories with missing revlogs, by
196 converting from and to Mercurial. Default is False.
203 converting from and to Mercurial. Default is False.
197
204
198 :convert.hg.saverev: store original revision ID in changeset
205 :convert.hg.saverev: store original revision ID in changeset
199 (forces target IDs to change). It takes a boolean argument and
206 (forces target IDs to change). It takes a boolean argument and
200 defaults to False.
207 defaults to False.
201
208
202 :convert.hg.startrev: specify the initial Mercurial revision.
209 :convert.hg.startrev: specify the initial Mercurial revision.
203 The default is 0.
210 The default is 0.
204
211
205 :convert.hg.revs: revset specifying the source revisions to convert.
212 :convert.hg.revs: revset specifying the source revisions to convert.
206
213
207 CVS Source
214 CVS Source
208 ##########
215 ##########
209
216
210 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
217 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
211 to indicate the starting point of what will be converted. Direct
218 to indicate the starting point of what will be converted. Direct
212 access to the repository files is not needed, unless of course the
219 access to the repository files is not needed, unless of course the
213 repository is ``:local:``. The conversion uses the top level
220 repository is ``:local:``. The conversion uses the top level
214 directory in the sandbox to find the CVS repository, and then uses
221 directory in the sandbox to find the CVS repository, and then uses
215 CVS rlog commands to find files to convert. This means that unless
222 CVS rlog commands to find files to convert. This means that unless
216 a filemap is given, all files under the starting directory will be
223 a filemap is given, all files under the starting directory will be
217 converted, and that any directory reorganization in the CVS
224 converted, and that any directory reorganization in the CVS
218 sandbox is ignored.
225 sandbox is ignored.
219
226
220 The following options can be used with ``--config``:
227 The following options can be used with ``--config``:
221
228
222 :convert.cvsps.cache: Set to False to disable remote log caching,
229 :convert.cvsps.cache: Set to False to disable remote log caching,
223 for testing and debugging purposes. Default is True.
230 for testing and debugging purposes. Default is True.
224
231
225 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
232 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
226 allowed between commits with identical user and log message in
233 allowed between commits with identical user and log message in
227 a single changeset. When very large files were checked in as
234 a single changeset. When very large files were checked in as
228 part of a changeset then the default may not be long enough.
235 part of a changeset then the default may not be long enough.
229 The default is 60.
236 The default is 60.
230
237
231 :convert.cvsps.logencoding: Specify encoding name to be used for
238 :convert.cvsps.logencoding: Specify encoding name to be used for
232 transcoding CVS log messages. Multiple encoding names can be
239 transcoding CVS log messages. Multiple encoding names can be
233 specified as a list (see :hg:`help config.Syntax`), but only
240 specified as a list (see :hg:`help config.Syntax`), but only
234 the first acceptable encoding in the list is used per CVS log
241 the first acceptable encoding in the list is used per CVS log
235 entries. This transcoding is executed before cvslog hook below.
242 entries. This transcoding is executed before cvslog hook below.
236
243
237 :convert.cvsps.mergeto: Specify a regular expression to which
244 :convert.cvsps.mergeto: Specify a regular expression to which
238 commit log messages are matched. If a match occurs, then the
245 commit log messages are matched. If a match occurs, then the
239 conversion process will insert a dummy revision merging the
246 conversion process will insert a dummy revision merging the
240 branch on which this log message occurs to the branch
247 branch on which this log message occurs to the branch
241 indicated in the regex. Default is ``{{mergetobranch
248 indicated in the regex. Default is ``{{mergetobranch
242 ([-\\w]+)}}``
249 ([-\\w]+)}}``
243
250
244 :convert.cvsps.mergefrom: Specify a regular expression to which
251 :convert.cvsps.mergefrom: Specify a regular expression to which
245 commit log messages are matched. If a match occurs, then the
252 commit log messages are matched. If a match occurs, then the
246 conversion process will add the most recent revision on the
253 conversion process will add the most recent revision on the
247 branch indicated in the regex as the second parent of the
254 branch indicated in the regex as the second parent of the
248 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
255 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
249
256
250 :convert.localtimezone: use local time (as determined by the TZ
257 :convert.localtimezone: use local time (as determined by the TZ
251 environment variable) for changeset date/times. The default
258 environment variable) for changeset date/times. The default
252 is False (use UTC).
259 is False (use UTC).
253
260
254 :hooks.cvslog: Specify a Python function to be called at the end of
261 :hooks.cvslog: Specify a Python function to be called at the end of
255 gathering the CVS log. The function is passed a list with the
262 gathering the CVS log. The function is passed a list with the
256 log entries, and can modify the entries in-place, or add or
263 log entries, and can modify the entries in-place, or add or
257 delete them.
264 delete them.
258
265
259 :hooks.cvschangesets: Specify a Python function to be called after
266 :hooks.cvschangesets: Specify a Python function to be called after
260 the changesets are calculated from the CVS log. The
267 the changesets are calculated from the CVS log. The
261 function is passed a list with the changeset entries, and can
268 function is passed a list with the changeset entries, and can
262 modify the changesets in-place, or add or delete them.
269 modify the changesets in-place, or add or delete them.
263
270
264 An additional "debugcvsps" Mercurial command allows the builtin
271 An additional "debugcvsps" Mercurial command allows the builtin
265 changeset merging code to be run without doing a conversion. Its
272 changeset merging code to be run without doing a conversion. Its
266 parameters and output are similar to that of cvsps 2.1. Please see
273 parameters and output are similar to that of cvsps 2.1. Please see
267 the command help for more details.
274 the command help for more details.
268
275
269 Subversion Source
276 Subversion Source
270 #################
277 #################
271
278
272 Subversion source detects classical trunk/branches/tags layouts.
279 Subversion source detects classical trunk/branches/tags layouts.
273 By default, the supplied ``svn://repo/path/`` source URL is
280 By default, the supplied ``svn://repo/path/`` source URL is
274 converted as a single branch. If ``svn://repo/path/trunk`` exists
281 converted as a single branch. If ``svn://repo/path/trunk`` exists
275 it replaces the default branch. If ``svn://repo/path/branches``
282 it replaces the default branch. If ``svn://repo/path/branches``
276 exists, its subdirectories are listed as possible branches. If
283 exists, its subdirectories are listed as possible branches. If
277 ``svn://repo/path/tags`` exists, it is looked for tags referencing
284 ``svn://repo/path/tags`` exists, it is looked for tags referencing
278 converted branches. Default ``trunk``, ``branches`` and ``tags``
285 converted branches. Default ``trunk``, ``branches`` and ``tags``
279 values can be overridden with following options. Set them to paths
286 values can be overridden with following options. Set them to paths
280 relative to the source URL, or leave them blank to disable auto
287 relative to the source URL, or leave them blank to disable auto
281 detection.
288 detection.
282
289
283 The following options can be set with ``--config``:
290 The following options can be set with ``--config``:
284
291
285 :convert.svn.branches: specify the directory containing branches.
292 :convert.svn.branches: specify the directory containing branches.
286 The default is ``branches``.
293 The default is ``branches``.
287
294
288 :convert.svn.tags: specify the directory containing tags. The
295 :convert.svn.tags: specify the directory containing tags. The
289 default is ``tags``.
296 default is ``tags``.
290
297
291 :convert.svn.trunk: specify the name of the trunk branch. The
298 :convert.svn.trunk: specify the name of the trunk branch. The
292 default is ``trunk``.
299 default is ``trunk``.
293
300
294 :convert.localtimezone: use local time (as determined by the TZ
301 :convert.localtimezone: use local time (as determined by the TZ
295 environment variable) for changeset date/times. The default
302 environment variable) for changeset date/times. The default
296 is False (use UTC).
303 is False (use UTC).
297
304
298 Source history can be retrieved starting at a specific revision,
305 Source history can be retrieved starting at a specific revision,
299 instead of being integrally converted. Only single branch
306 instead of being integrally converted. Only single branch
300 conversions are supported.
307 conversions are supported.
301
308
302 :convert.svn.startrev: specify start Subversion revision number.
309 :convert.svn.startrev: specify start Subversion revision number.
303 The default is 0.
310 The default is 0.
304
311
305 Git Source
312 Git Source
306 ##########
313 ##########
307
314
308 The Git importer converts commits from all reachable branches (refs
315 The Git importer converts commits from all reachable branches (refs
309 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
316 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
310 Branches are converted to bookmarks with the same name, with the
317 Branches are converted to bookmarks with the same name, with the
311 leading 'refs/heads' stripped. Git submodules are converted to Git
318 leading 'refs/heads' stripped. Git submodules are converted to Git
312 subrepos in Mercurial.
319 subrepos in Mercurial.
313
320
314 The following options can be set with ``--config``:
321 The following options can be set with ``--config``:
315
322
316 :convert.git.similarity: specify how similar files modified in a
323 :convert.git.similarity: specify how similar files modified in a
317 commit must be to be imported as renames or copies, as a
324 commit must be to be imported as renames or copies, as a
318 percentage between ``0`` (disabled) and ``100`` (files must be
325 percentage between ``0`` (disabled) and ``100`` (files must be
319 identical). For example, ``90`` means that a delete/add pair will
326 identical). For example, ``90`` means that a delete/add pair will
320 be imported as a rename if more than 90% of the file hasn't
327 be imported as a rename if more than 90% of the file hasn't
321 changed. The default is ``50``.
328 changed. The default is ``50``.
322
329
323 :convert.git.findcopiesharder: while detecting copies, look at all
330 :convert.git.findcopiesharder: while detecting copies, look at all
324 files in the working copy instead of just changed ones. This
331 files in the working copy instead of just changed ones. This
325 is very expensive for large projects, and is only effective when
332 is very expensive for large projects, and is only effective when
326 ``convert.git.similarity`` is greater than 0. The default is False.
333 ``convert.git.similarity`` is greater than 0. The default is False.
327
334
328 :convert.git.renamelimit: perform rename and copy detection up to this
335 :convert.git.renamelimit: perform rename and copy detection up to this
329 many changed files in a commit. Increasing this will make rename
336 many changed files in a commit. Increasing this will make rename
330 and copy detection more accurate but will significantly slow down
337 and copy detection more accurate but will significantly slow down
331 computation on large projects. The option is only relevant if
338 computation on large projects. The option is only relevant if
332 ``convert.git.similarity`` is greater than 0. The default is
339 ``convert.git.similarity`` is greater than 0. The default is
333 ``400``.
340 ``400``.
334
341
335 :convert.git.committeractions: list of actions to take when processing
342 :convert.git.committeractions: list of actions to take when processing
336 author and committer values.
343 author and committer values.
337
344
338 Git commits have separate author (who wrote the commit) and committer
345 Git commits have separate author (who wrote the commit) and committer
339 (who applied the commit) fields. Not all destinations support separate
346 (who applied the commit) fields. Not all destinations support separate
340 author and committer fields (including Mercurial). This config option
347 author and committer fields (including Mercurial). This config option
341 controls what to do with these author and committer fields during
348 controls what to do with these author and committer fields during
342 conversion.
349 conversion.
343
350
344 A value of ``messagedifferent`` will append a ``committer: ...``
351 A value of ``messagedifferent`` will append a ``committer: ...``
345 line to the commit message if the Git committer is different from the
352 line to the commit message if the Git committer is different from the
346 author. The prefix of that line can be specified using the syntax
353 author. The prefix of that line can be specified using the syntax
347 ``messagedifferent=<prefix>``. e.g. ``messagedifferent=git-committer:``.
354 ``messagedifferent=<prefix>``. e.g. ``messagedifferent=git-committer:``.
348 When a prefix is specified, a space will always be inserted between the
355 When a prefix is specified, a space will always be inserted between the
349 prefix and the value.
356 prefix and the value.
350
357
351 ``messagealways`` behaves like ``messagedifferent`` except it will
358 ``messagealways`` behaves like ``messagedifferent`` except it will
352 always result in a ``committer: ...`` line being appended to the commit
359 always result in a ``committer: ...`` line being appended to the commit
353 message. This value is mutually exclusive with ``messagedifferent``.
360 message. This value is mutually exclusive with ``messagedifferent``.
354
361
355 ``dropcommitter`` will remove references to the committer. Only
362 ``dropcommitter`` will remove references to the committer. Only
356 references to the author will remain. Actions that add references
363 references to the author will remain. Actions that add references
357 to the committer will have no effect when this is set.
364 to the committer will have no effect when this is set.
358
365
359 ``replaceauthor`` will replace the value of the author field with
366 ``replaceauthor`` will replace the value of the author field with
360 the committer. Other actions that add references to the committer
367 the committer. Other actions that add references to the committer
361 will still take effect when this is set.
368 will still take effect when this is set.
362
369
363 The default is ``messagedifferent``.
370 The default is ``messagedifferent``.
364
371
365 :convert.git.extrakeys: list of extra keys from commit metadata to copy to
372 :convert.git.extrakeys: list of extra keys from commit metadata to copy to
366 the destination. Some Git repositories store extra metadata in commits.
373 the destination. Some Git repositories store extra metadata in commits.
367 By default, this non-default metadata will be lost during conversion.
374 By default, this non-default metadata will be lost during conversion.
368 Setting this config option can retain that metadata. Some built-in
375 Setting this config option can retain that metadata. Some built-in
369 keys such as ``parent`` and ``branch`` are not allowed to be copied.
376 keys such as ``parent`` and ``branch`` are not allowed to be copied.
370
377
371 :convert.git.remoteprefix: remote refs are converted as bookmarks with
378 :convert.git.remoteprefix: remote refs are converted as bookmarks with
372 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
379 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
373 is 'remote'.
380 is 'remote'.
374
381
375 :convert.git.saverev: whether to store the original Git commit ID in the
382 :convert.git.saverev: whether to store the original Git commit ID in the
376 metadata of the destination commit. The default is True.
383 metadata of the destination commit. The default is True.
377
384
378 :convert.git.skipsubmodules: does not convert root level .gitmodules files
385 :convert.git.skipsubmodules: does not convert root level .gitmodules files
379 or files with 160000 mode indicating a submodule. Default is False.
386 or files with 160000 mode indicating a submodule. Default is False.
380
387
381 Perforce Source
388 Perforce Source
382 ###############
389 ###############
383
390
384 The Perforce (P4) importer can be given a p4 depot path or a
391 The Perforce (P4) importer can be given a p4 depot path or a
385 client specification as source. It will convert all files in the
392 client specification as source. It will convert all files in the
386 source to a flat Mercurial repository, ignoring labels, branches
393 source to a flat Mercurial repository, ignoring labels, branches
387 and integrations. Note that when a depot path is given you then
394 and integrations. Note that when a depot path is given you then
388 usually should specify a target directory, because otherwise the
395 usually should specify a target directory, because otherwise the
389 target may be named ``...-hg``.
396 target may be named ``...-hg``.
390
397
391 The following options can be set with ``--config``:
398 The following options can be set with ``--config``:
392
399
393 :convert.p4.encoding: specify the encoding to use when decoding standard
400 :convert.p4.encoding: specify the encoding to use when decoding standard
394 output of the Perforce command line tool. The default is default system
401 output of the Perforce command line tool. The default is default system
395 encoding.
402 encoding.
396
403
397 :convert.p4.startrev: specify initial Perforce revision (a
404 :convert.p4.startrev: specify initial Perforce revision (a
398 Perforce changelist number).
405 Perforce changelist number).
399
406
400 Mercurial Destination
407 Mercurial Destination
401 #####################
408 #####################
402
409
403 The Mercurial destination will recognize Mercurial subrepositories in the
410 The Mercurial destination will recognize Mercurial subrepositories in the
404 destination directory, and update the .hgsubstate file automatically if the
411 destination directory, and update the .hgsubstate file automatically if the
405 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
412 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
406 Converting a repository with subrepositories requires converting a single
413 Converting a repository with subrepositories requires converting a single
407 repository at a time, from the bottom up.
414 repository at a time, from the bottom up.
408
415
409 .. container:: verbose
416 .. container:: verbose
410
417
411 An example showing how to convert a repository with subrepositories::
418 An example showing how to convert a repository with subrepositories::
412
419
413 # so convert knows the type when it sees a non empty destination
420 # so convert knows the type when it sees a non empty destination
414 $ hg init converted
421 $ hg init converted
415
422
416 $ hg convert orig/sub1 converted/sub1
423 $ hg convert orig/sub1 converted/sub1
417 $ hg convert orig/sub2 converted/sub2
424 $ hg convert orig/sub2 converted/sub2
418 $ hg convert orig converted
425 $ hg convert orig converted
419
426
420 The following options are supported:
427 The following options are supported:
421
428
422 :convert.hg.clonebranches: dispatch source branches in separate
429 :convert.hg.clonebranches: dispatch source branches in separate
423 clones. The default is False.
430 clones. The default is False.
424
431
425 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
432 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
426 ``default``.
433 ``default``.
427
434
428 :convert.hg.usebranchnames: preserve branch names. The default is
435 :convert.hg.usebranchnames: preserve branch names. The default is
429 True.
436 True.
430
437
431 :convert.hg.sourcename: records the given string as a 'convert_source' extra
438 :convert.hg.sourcename: records the given string as a 'convert_source' extra
432 value on each commit made in the target repository. The default is None.
439 value on each commit made in the target repository. The default is None.
433
440
434 All Destinations
441 All Destinations
435 ################
442 ################
436
443
437 All destination types accept the following options:
444 All destination types accept the following options:
438
445
439 :convert.skiptags: does not convert tags from the source repo to the target
446 :convert.skiptags: does not convert tags from the source repo to the target
440 repo. The default is False.
447 repo. The default is False.
441 """
448 """
442 return convcmd.convert(ui, src, dest, revmapfile, **opts)
449 return convcmd.convert(ui, src, dest, revmapfile, **opts)
443
450
444 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
451 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
445 def debugsvnlog(ui, **opts):
452 def debugsvnlog(ui, **opts):
446 return subversion.debugsvnlog(ui, **opts)
453 return subversion.debugsvnlog(ui, **opts)
447
454
448 @command('debugcvsps',
455 @command('debugcvsps',
449 [
456 [
450 # Main options shared with cvsps-2.1
457 # Main options shared with cvsps-2.1
451 ('b', 'branches', [], _('only return changes on specified branches')),
458 ('b', 'branches', [], _('only return changes on specified branches')),
452 ('p', 'prefix', '', _('prefix to remove from file names')),
459 ('p', 'prefix', '', _('prefix to remove from file names')),
453 ('r', 'revisions', [],
460 ('r', 'revisions', [],
454 _('only return changes after or between specified tags')),
461 _('only return changes after or between specified tags')),
455 ('u', 'update-cache', None, _("update cvs log cache")),
462 ('u', 'update-cache', None, _("update cvs log cache")),
456 ('x', 'new-cache', None, _("create new cvs log cache")),
463 ('x', 'new-cache', None, _("create new cvs log cache")),
457 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
464 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
458 ('', 'root', '', _('specify cvsroot')),
465 ('', 'root', '', _('specify cvsroot')),
459 # Options specific to builtin cvsps
466 # Options specific to builtin cvsps
460 ('', 'parents', '', _('show parent changesets')),
467 ('', 'parents', '', _('show parent changesets')),
461 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
468 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
462 # Options that are ignored for compatibility with cvsps-2.1
469 # Options that are ignored for compatibility with cvsps-2.1
463 ('A', 'cvs-direct', None, _('ignored for compatibility')),
470 ('A', 'cvs-direct', None, _('ignored for compatibility')),
464 ],
471 ],
465 _('hg debugcvsps [OPTION]... [PATH]...'),
472 _('hg debugcvsps [OPTION]... [PATH]...'),
466 norepo=True)
473 norepo=True)
467 def debugcvsps(ui, *args, **opts):
474 def debugcvsps(ui, *args, **opts):
468 '''create changeset information from CVS
475 '''create changeset information from CVS
469
476
470 This command is intended as a debugging tool for the CVS to
477 This command is intended as a debugging tool for the CVS to
471 Mercurial converter, and can be used as a direct replacement for
478 Mercurial converter, and can be used as a direct replacement for
472 cvsps.
479 cvsps.
473
480
474 Hg debugcvsps reads the CVS rlog for current directory (or any
481 Hg debugcvsps reads the CVS rlog for current directory (or any
475 named directory) in the CVS repository, and converts the log to a
482 named directory) in the CVS repository, and converts the log to a
476 series of changesets based on matching commit log entries and
483 series of changesets based on matching commit log entries and
477 dates.'''
484 dates.'''
478 return cvsps.debugcvsps(ui, *args, **opts)
485 return cvsps.debugcvsps(ui, *args, **opts)
479
486
480 def kwconverted(ctx, name):
487 def kwconverted(ctx, name):
481 rev = ctx.extra().get('convert_revision', '')
488 rev = ctx.extra().get('convert_revision', '')
482 if rev.startswith('svn:'):
489 if rev.startswith('svn:'):
483 if name == 'svnrev':
490 if name == 'svnrev':
484 return str(subversion.revsplit(rev)[2])
491 return str(subversion.revsplit(rev)[2])
485 elif name == 'svnpath':
492 elif name == 'svnpath':
486 return subversion.revsplit(rev)[1]
493 return subversion.revsplit(rev)[1]
487 elif name == 'svnuuid':
494 elif name == 'svnuuid':
488 return subversion.revsplit(rev)[0]
495 return subversion.revsplit(rev)[0]
489 return rev
496 return rev
490
497
491 templatekeyword = registrar.templatekeyword()
498 templatekeyword = registrar.templatekeyword()
492
499
493 @templatekeyword('svnrev')
500 @templatekeyword('svnrev')
494 def kwsvnrev(repo, ctx, **args):
501 def kwsvnrev(repo, ctx, **args):
495 """String. Converted subversion revision number."""
502 """String. Converted subversion revision number."""
496 return kwconverted(ctx, 'svnrev')
503 return kwconverted(ctx, 'svnrev')
497
504
498 @templatekeyword('svnpath')
505 @templatekeyword('svnpath')
499 def kwsvnpath(repo, ctx, **args):
506 def kwsvnpath(repo, ctx, **args):
500 """String. Converted subversion revision project path."""
507 """String. Converted subversion revision project path."""
501 return kwconverted(ctx, 'svnpath')
508 return kwconverted(ctx, 'svnpath')
502
509
503 @templatekeyword('svnuuid')
510 @templatekeyword('svnuuid')
504 def kwsvnuuid(repo, ctx, **args):
511 def kwsvnuuid(repo, ctx, **args):
505 """String. Converted subversion revision repository identifier."""
512 """String. Converted subversion revision repository identifier."""
506 return kwconverted(ctx, 'svnuuid')
513 return kwconverted(ctx, 'svnuuid')
507
514
508 # tell hggettext to extract docstrings from these functions:
515 # tell hggettext to extract docstrings from these functions:
509 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
516 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
@@ -1,297 +1,297 b''
1 # cvs.py: CVS conversion code inspired by hg-cvs-import and git-cvsimport
1 # cvs.py: CVS conversion code inspired by hg-cvs-import and git-cvsimport
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import errno
9 import errno
10 import os
10 import os
11 import re
11 import re
12 import socket
12 import socket
13
13
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15 from mercurial import (
15 from mercurial import (
16 encoding,
16 encoding,
17 error,
17 error,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21
21
22 from . import (
22 from . import (
23 common,
23 common,
24 cvsps,
24 cvsps,
25 )
25 )
26
26
27 stringio = util.stringio
27 stringio = util.stringio
28 checktool = common.checktool
28 checktool = common.checktool
29 commit = common.commit
29 commit = common.commit
30 converter_source = common.converter_source
30 converter_source = common.converter_source
31 makedatetimestamp = common.makedatetimestamp
31 makedatetimestamp = common.makedatetimestamp
32 NoRepo = common.NoRepo
32 NoRepo = common.NoRepo
33
33
34 class convert_cvs(converter_source):
34 class convert_cvs(converter_source):
35 def __init__(self, ui, path, revs=None):
35 def __init__(self, ui, path, revs=None):
36 super(convert_cvs, self).__init__(ui, path, revs=revs)
36 super(convert_cvs, self).__init__(ui, path, revs=revs)
37
37
38 cvs = os.path.join(path, "CVS")
38 cvs = os.path.join(path, "CVS")
39 if not os.path.exists(cvs):
39 if not os.path.exists(cvs):
40 raise NoRepo(_("%s does not look like a CVS checkout") % path)
40 raise NoRepo(_("%s does not look like a CVS checkout") % path)
41
41
42 checktool('cvs')
42 checktool('cvs')
43
43
44 self.changeset = None
44 self.changeset = None
45 self.files = {}
45 self.files = {}
46 self.tags = {}
46 self.tags = {}
47 self.lastbranch = {}
47 self.lastbranch = {}
48 self.socket = None
48 self.socket = None
49 self.cvsroot = open(os.path.join(cvs, "Root")).read()[:-1]
49 self.cvsroot = open(os.path.join(cvs, "Root")).read()[:-1]
50 self.cvsrepo = open(os.path.join(cvs, "Repository")).read()[:-1]
50 self.cvsrepo = open(os.path.join(cvs, "Repository")).read()[:-1]
51 self.encoding = encoding.encoding
51 self.encoding = encoding.encoding
52
52
53 self._connect()
53 self._connect()
54
54
55 def _parse(self):
55 def _parse(self):
56 if self.changeset is not None:
56 if self.changeset is not None:
57 return
57 return
58 self.changeset = {}
58 self.changeset = {}
59
59
60 maxrev = 0
60 maxrev = 0
61 if self.revs:
61 if self.revs:
62 if len(self.revs) > 1:
62 if len(self.revs) > 1:
63 raise error.Abort(_('cvs source does not support specifying '
63 raise error.Abort(_('cvs source does not support specifying '
64 'multiple revs'))
64 'multiple revs'))
65 # TODO: handle tags
65 # TODO: handle tags
66 try:
66 try:
67 # patchset number?
67 # patchset number?
68 maxrev = int(self.revs[0])
68 maxrev = int(self.revs[0])
69 except ValueError:
69 except ValueError:
70 raise error.Abort(_('revision %s is not a patchset number')
70 raise error.Abort(_('revision %s is not a patchset number')
71 % self.revs[0])
71 % self.revs[0])
72
72
73 d = pycompat.getcwd()
73 d = pycompat.getcwd()
74 try:
74 try:
75 os.chdir(self.path)
75 os.chdir(self.path)
76 id = None
76 id = None
77
77
78 cache = 'update'
78 cache = 'update'
79 if not self.ui.configbool('convert', 'cvsps.cache', True):
79 if not self.ui.configbool('convert', 'cvsps.cache'):
80 cache = None
80 cache = None
81 db = cvsps.createlog(self.ui, cache=cache)
81 db = cvsps.createlog(self.ui, cache=cache)
82 db = cvsps.createchangeset(self.ui, db,
82 db = cvsps.createchangeset(self.ui, db,
83 fuzz=int(self.ui.config('convert', 'cvsps.fuzz', 60)),
83 fuzz=int(self.ui.config('convert', 'cvsps.fuzz', 60)),
84 mergeto=self.ui.config('convert', 'cvsps.mergeto', None),
84 mergeto=self.ui.config('convert', 'cvsps.mergeto', None),
85 mergefrom=self.ui.config('convert', 'cvsps.mergefrom', None))
85 mergefrom=self.ui.config('convert', 'cvsps.mergefrom', None))
86
86
87 for cs in db:
87 for cs in db:
88 if maxrev and cs.id > maxrev:
88 if maxrev and cs.id > maxrev:
89 break
89 break
90 id = str(cs.id)
90 id = str(cs.id)
91 cs.author = self.recode(cs.author)
91 cs.author = self.recode(cs.author)
92 self.lastbranch[cs.branch] = id
92 self.lastbranch[cs.branch] = id
93 cs.comment = self.recode(cs.comment)
93 cs.comment = self.recode(cs.comment)
94 if self.ui.configbool('convert', 'localtimezone'):
94 if self.ui.configbool('convert', 'localtimezone'):
95 cs.date = makedatetimestamp(cs.date[0])
95 cs.date = makedatetimestamp(cs.date[0])
96 date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2')
96 date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2')
97 self.tags.update(dict.fromkeys(cs.tags, id))
97 self.tags.update(dict.fromkeys(cs.tags, id))
98
98
99 files = {}
99 files = {}
100 for f in cs.entries:
100 for f in cs.entries:
101 files[f.file] = "%s%s" % ('.'.join([str(x)
101 files[f.file] = "%s%s" % ('.'.join([str(x)
102 for x in f.revision]),
102 for x in f.revision]),
103 ['', '(DEAD)'][f.dead])
103 ['', '(DEAD)'][f.dead])
104
104
105 # add current commit to set
105 # add current commit to set
106 c = commit(author=cs.author, date=date,
106 c = commit(author=cs.author, date=date,
107 parents=[str(p.id) for p in cs.parents],
107 parents=[str(p.id) for p in cs.parents],
108 desc=cs.comment, branch=cs.branch or '')
108 desc=cs.comment, branch=cs.branch or '')
109 self.changeset[id] = c
109 self.changeset[id] = c
110 self.files[id] = files
110 self.files[id] = files
111
111
112 self.heads = self.lastbranch.values()
112 self.heads = self.lastbranch.values()
113 finally:
113 finally:
114 os.chdir(d)
114 os.chdir(d)
115
115
116 def _connect(self):
116 def _connect(self):
117 root = self.cvsroot
117 root = self.cvsroot
118 conntype = None
118 conntype = None
119 user, host = None, None
119 user, host = None, None
120 cmd = ['cvs', 'server']
120 cmd = ['cvs', 'server']
121
121
122 self.ui.status(_("connecting to %s\n") % root)
122 self.ui.status(_("connecting to %s\n") % root)
123
123
124 if root.startswith(":pserver:"):
124 if root.startswith(":pserver:"):
125 root = root[9:]
125 root = root[9:]
126 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
126 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
127 root)
127 root)
128 if m:
128 if m:
129 conntype = "pserver"
129 conntype = "pserver"
130 user, passw, serv, port, root = m.groups()
130 user, passw, serv, port, root = m.groups()
131 if not user:
131 if not user:
132 user = "anonymous"
132 user = "anonymous"
133 if not port:
133 if not port:
134 port = 2401
134 port = 2401
135 else:
135 else:
136 port = int(port)
136 port = int(port)
137 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
137 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
138 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
138 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
139
139
140 if not passw:
140 if not passw:
141 passw = "A"
141 passw = "A"
142 cvspass = os.path.expanduser("~/.cvspass")
142 cvspass = os.path.expanduser("~/.cvspass")
143 try:
143 try:
144 pf = open(cvspass)
144 pf = open(cvspass)
145 for line in pf.read().splitlines():
145 for line in pf.read().splitlines():
146 part1, part2 = line.split(' ', 1)
146 part1, part2 = line.split(' ', 1)
147 # /1 :pserver:user@example.com:2401/cvsroot/foo
147 # /1 :pserver:user@example.com:2401/cvsroot/foo
148 # Ah<Z
148 # Ah<Z
149 if part1 == '/1':
149 if part1 == '/1':
150 part1, part2 = part2.split(' ', 1)
150 part1, part2 = part2.split(' ', 1)
151 format = format1
151 format = format1
152 # :pserver:user@example.com:/cvsroot/foo Ah<Z
152 # :pserver:user@example.com:/cvsroot/foo Ah<Z
153 else:
153 else:
154 format = format0
154 format = format0
155 if part1 == format:
155 if part1 == format:
156 passw = part2
156 passw = part2
157 break
157 break
158 pf.close()
158 pf.close()
159 except IOError as inst:
159 except IOError as inst:
160 if inst.errno != errno.ENOENT:
160 if inst.errno != errno.ENOENT:
161 if not getattr(inst, 'filename', None):
161 if not getattr(inst, 'filename', None):
162 inst.filename = cvspass
162 inst.filename = cvspass
163 raise
163 raise
164
164
165 sck = socket.socket()
165 sck = socket.socket()
166 sck.connect((serv, port))
166 sck.connect((serv, port))
167 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
167 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
168 "END AUTH REQUEST", ""]))
168 "END AUTH REQUEST", ""]))
169 if sck.recv(128) != "I LOVE YOU\n":
169 if sck.recv(128) != "I LOVE YOU\n":
170 raise error.Abort(_("CVS pserver authentication failed"))
170 raise error.Abort(_("CVS pserver authentication failed"))
171
171
172 self.writep = self.readp = sck.makefile('r+')
172 self.writep = self.readp = sck.makefile('r+')
173
173
174 if not conntype and root.startswith(":local:"):
174 if not conntype and root.startswith(":local:"):
175 conntype = "local"
175 conntype = "local"
176 root = root[7:]
176 root = root[7:]
177
177
178 if not conntype:
178 if not conntype:
179 # :ext:user@host/home/user/path/to/cvsroot
179 # :ext:user@host/home/user/path/to/cvsroot
180 if root.startswith(":ext:"):
180 if root.startswith(":ext:"):
181 root = root[5:]
181 root = root[5:]
182 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
182 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
183 # Do not take Windows path "c:\foo\bar" for a connection strings
183 # Do not take Windows path "c:\foo\bar" for a connection strings
184 if os.path.isdir(root) or not m:
184 if os.path.isdir(root) or not m:
185 conntype = "local"
185 conntype = "local"
186 else:
186 else:
187 conntype = "rsh"
187 conntype = "rsh"
188 user, host, root = m.group(1), m.group(2), m.group(3)
188 user, host, root = m.group(1), m.group(2), m.group(3)
189
189
190 if conntype != "pserver":
190 if conntype != "pserver":
191 if conntype == "rsh":
191 if conntype == "rsh":
192 rsh = encoding.environ.get("CVS_RSH") or "ssh"
192 rsh = encoding.environ.get("CVS_RSH") or "ssh"
193 if user:
193 if user:
194 cmd = [rsh, '-l', user, host] + cmd
194 cmd = [rsh, '-l', user, host] + cmd
195 else:
195 else:
196 cmd = [rsh, host] + cmd
196 cmd = [rsh, host] + cmd
197
197
198 # popen2 does not support argument lists under Windows
198 # popen2 does not support argument lists under Windows
199 cmd = [util.shellquote(arg) for arg in cmd]
199 cmd = [util.shellquote(arg) for arg in cmd]
200 cmd = util.quotecommand(' '.join(cmd))
200 cmd = util.quotecommand(' '.join(cmd))
201 self.writep, self.readp = util.popen2(cmd)
201 self.writep, self.readp = util.popen2(cmd)
202
202
203 self.realroot = root
203 self.realroot = root
204
204
205 self.writep.write("Root %s\n" % root)
205 self.writep.write("Root %s\n" % root)
206 self.writep.write("Valid-responses ok error Valid-requests Mode"
206 self.writep.write("Valid-responses ok error Valid-requests Mode"
207 " M Mbinary E Checked-in Created Updated"
207 " M Mbinary E Checked-in Created Updated"
208 " Merged Removed\n")
208 " Merged Removed\n")
209 self.writep.write("valid-requests\n")
209 self.writep.write("valid-requests\n")
210 self.writep.flush()
210 self.writep.flush()
211 r = self.readp.readline()
211 r = self.readp.readline()
212 if not r.startswith("Valid-requests"):
212 if not r.startswith("Valid-requests"):
213 raise error.Abort(_('unexpected response from CVS server '
213 raise error.Abort(_('unexpected response from CVS server '
214 '(expected "Valid-requests", but got %r)')
214 '(expected "Valid-requests", but got %r)')
215 % r)
215 % r)
216 if "UseUnchanged" in r:
216 if "UseUnchanged" in r:
217 self.writep.write("UseUnchanged\n")
217 self.writep.write("UseUnchanged\n")
218 self.writep.flush()
218 self.writep.flush()
219 r = self.readp.readline()
219 r = self.readp.readline()
220
220
221 def getheads(self):
221 def getheads(self):
222 self._parse()
222 self._parse()
223 return self.heads
223 return self.heads
224
224
225 def getfile(self, name, rev):
225 def getfile(self, name, rev):
226
226
227 def chunkedread(fp, count):
227 def chunkedread(fp, count):
228 # file-objects returned by socket.makefile() do not handle
228 # file-objects returned by socket.makefile() do not handle
229 # large read() requests very well.
229 # large read() requests very well.
230 chunksize = 65536
230 chunksize = 65536
231 output = stringio()
231 output = stringio()
232 while count > 0:
232 while count > 0:
233 data = fp.read(min(count, chunksize))
233 data = fp.read(min(count, chunksize))
234 if not data:
234 if not data:
235 raise error.Abort(_("%d bytes missing from remote file")
235 raise error.Abort(_("%d bytes missing from remote file")
236 % count)
236 % count)
237 count -= len(data)
237 count -= len(data)
238 output.write(data)
238 output.write(data)
239 return output.getvalue()
239 return output.getvalue()
240
240
241 self._parse()
241 self._parse()
242 if rev.endswith("(DEAD)"):
242 if rev.endswith("(DEAD)"):
243 return None, None
243 return None, None
244
244
245 args = ("-N -P -kk -r %s --" % rev).split()
245 args = ("-N -P -kk -r %s --" % rev).split()
246 args.append(self.cvsrepo + '/' + name)
246 args.append(self.cvsrepo + '/' + name)
247 for x in args:
247 for x in args:
248 self.writep.write("Argument %s\n" % x)
248 self.writep.write("Argument %s\n" % x)
249 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
249 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
250 self.writep.flush()
250 self.writep.flush()
251
251
252 data = ""
252 data = ""
253 mode = None
253 mode = None
254 while True:
254 while True:
255 line = self.readp.readline()
255 line = self.readp.readline()
256 if line.startswith("Created ") or line.startswith("Updated "):
256 if line.startswith("Created ") or line.startswith("Updated "):
257 self.readp.readline() # path
257 self.readp.readline() # path
258 self.readp.readline() # entries
258 self.readp.readline() # entries
259 mode = self.readp.readline()[:-1]
259 mode = self.readp.readline()[:-1]
260 count = int(self.readp.readline()[:-1])
260 count = int(self.readp.readline()[:-1])
261 data = chunkedread(self.readp, count)
261 data = chunkedread(self.readp, count)
262 elif line.startswith(" "):
262 elif line.startswith(" "):
263 data += line[1:]
263 data += line[1:]
264 elif line.startswith("M "):
264 elif line.startswith("M "):
265 pass
265 pass
266 elif line.startswith("Mbinary "):
266 elif line.startswith("Mbinary "):
267 count = int(self.readp.readline()[:-1])
267 count = int(self.readp.readline()[:-1])
268 data = chunkedread(self.readp, count)
268 data = chunkedread(self.readp, count)
269 else:
269 else:
270 if line == "ok\n":
270 if line == "ok\n":
271 if mode is None:
271 if mode is None:
272 raise error.Abort(_('malformed response from CVS'))
272 raise error.Abort(_('malformed response from CVS'))
273 return (data, "x" in mode and "x" or "")
273 return (data, "x" in mode and "x" or "")
274 elif line.startswith("E "):
274 elif line.startswith("E "):
275 self.ui.warn(_("cvs server: %s\n") % line[2:])
275 self.ui.warn(_("cvs server: %s\n") % line[2:])
276 elif line.startswith("Remove"):
276 elif line.startswith("Remove"):
277 self.readp.readline()
277 self.readp.readline()
278 else:
278 else:
279 raise error.Abort(_("unknown CVS response: %s") % line)
279 raise error.Abort(_("unknown CVS response: %s") % line)
280
280
281 def getchanges(self, rev, full):
281 def getchanges(self, rev, full):
282 if full:
282 if full:
283 raise error.Abort(_("convert from cvs does not support --full"))
283 raise error.Abort(_("convert from cvs does not support --full"))
284 self._parse()
284 self._parse()
285 return sorted(self.files[rev].iteritems()), {}, set()
285 return sorted(self.files[rev].iteritems()), {}, set()
286
286
287 def getcommit(self, rev):
287 def getcommit(self, rev):
288 self._parse()
288 self._parse()
289 return self.changeset[rev]
289 return self.changeset[rev]
290
290
291 def gettags(self):
291 def gettags(self):
292 self._parse()
292 self._parse()
293 return self.tags
293 return self.tags
294
294
295 def getchangedfiles(self, rev, i):
295 def getchangedfiles(self, rev, i):
296 self._parse()
296 self._parse()
297 return sorted(self.files[rev])
297 return sorted(self.files[rev])
General Comments 0
You need to be logged in to leave comments. Login now