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