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