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