##// END OF EJS Templates
merge with stable
Augie Fackler -
r35164:ee64e677 merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,606 +1,509 b''
1 # convert.py Foreign SCM converter
1 # convert.py Foreign SCM converter
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''import revisions from foreign VCS repositories into Mercurial'''
8 '''import revisions from foreign VCS repositories into Mercurial'''
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13 from mercurial import (
13 from mercurial import (
14 registrar,
14 registrar,
15 )
15 )
16
16
17 from . import (
17 from . import (
18 convcmd,
18 convcmd,
19 cvsps,
19 cvsps,
20 subversion,
20 subversion,
21 )
21 )
22
22
23 cmdtable = {}
23 cmdtable = {}
24 command = registrar.command(cmdtable)
24 command = registrar.command(cmdtable)
25 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
25 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
26 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
26 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
27 # be specifying the version(s) of Mercurial they are tested with, or
27 # be specifying the version(s) of Mercurial they are tested with, or
28 # leave the attribute unspecified.
28 # leave the attribute unspecified.
29 testedwith = 'ships-with-hg-core'
29 testedwith = 'ships-with-hg-core'
30
30
31 configtable = {}
32 configitem = registrar.configitem(configtable)
33
34 configitem('convert', 'cvsps.cache',
35 default=True,
36 )
37 configitem('convert', 'cvsps.fuzz',
38 default=60,
39 )
40 configitem('convert', 'cvsps.logencoding',
41 default=None,
42 )
43 configitem('convert', 'cvsps.mergefrom',
44 default=None,
45 )
46 configitem('convert', 'cvsps.mergeto',
47 default=None,
48 )
49 configitem('convert', 'git.committeractions',
50 default=lambda: ['messagedifferent'],
51 )
52 configitem('convert', 'git.extrakeys',
53 default=list,
54 )
55 configitem('convert', 'git.findcopiesharder',
56 default=False,
57 )
58 configitem('convert', 'git.remoteprefix',
59 default='remote',
60 )
61 configitem('convert', 'git.renamelimit',
62 default=400,
63 )
64 configitem('convert', 'git.saverev',
65 default=True,
66 )
67 configitem('convert', 'git.similarity',
68 default=50,
69 )
70 configitem('convert', 'git.skipsubmodules',
71 default=False,
72 )
73 configitem('convert', 'hg.clonebranches',
74 default=False,
75 )
76 configitem('convert', 'hg.ignoreerrors',
77 default=False,
78 )
79 configitem('convert', 'hg.revs',
80 default=None,
81 )
82 configitem('convert', 'hg.saverev',
83 default=False,
84 )
85 configitem('convert', 'hg.sourcename',
86 default=None,
87 )
88 configitem('convert', 'hg.startrev',
89 default=None,
90 )
91 configitem('convert', 'hg.tagsbranch',
92 default='default',
93 )
94 configitem('convert', 'hg.usebranchnames',
95 default=True,
96 )
97 configitem('convert', 'ignoreancestorcheck',
98 default=False,
99 )
100 configitem('convert', 'localtimezone',
101 default=False,
102 )
103 configitem('convert', 'p4.encoding',
104 default=lambda: convcmd.orig_encoding,
105 )
106 configitem('convert', 'p4.startrev',
107 default=0,
108 )
109 configitem('convert', 'skiptags',
110 default=False,
111 )
112 configitem('convert', 'svn.debugsvnlog',
113 default=True,
114 )
115 configitem('convert', 'svn.trunk',
116 default=None,
117 )
118 configitem('convert', 'svn.tags',
119 default=None,
120 )
121 configitem('convert', 'svn.branches',
122 default=None,
123 )
124 configitem('convert', 'svn.startrev',
125 default=0,
126 )
127
128 # Commands definition was moved elsewhere to ease demandload job.
31 # Commands definition was moved elsewhere to ease demandload job.
129
32
130 @command('convert',
33 @command('convert',
131 [('', 'authors', '',
34 [('', 'authors', '',
132 _('username mapping filename (DEPRECATED) (use --authormap instead)'),
35 _('username mapping filename (DEPRECATED) (use --authormap instead)'),
133 _('FILE')),
36 _('FILE')),
134 ('s', 'source-type', '', _('source repository type'), _('TYPE')),
37 ('s', 'source-type', '', _('source repository type'), _('TYPE')),
135 ('d', 'dest-type', '', _('destination repository type'), _('TYPE')),
38 ('d', 'dest-type', '', _('destination repository type'), _('TYPE')),
136 ('r', 'rev', [], _('import up to source revision REV'), _('REV')),
39 ('r', 'rev', [], _('import up to source revision REV'), _('REV')),
137 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
40 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
138 ('', 'filemap', '', _('remap file names using contents of file'),
41 ('', 'filemap', '', _('remap file names using contents of file'),
139 _('FILE')),
42 _('FILE')),
140 ('', 'full', None,
43 ('', 'full', None,
141 _('apply filemap changes by converting all files again')),
44 _('apply filemap changes by converting all files again')),
142 ('', 'splicemap', '', _('splice synthesized history into place'),
45 ('', 'splicemap', '', _('splice synthesized history into place'),
143 _('FILE')),
46 _('FILE')),
144 ('', 'branchmap', '', _('change branch names while converting'),
47 ('', 'branchmap', '', _('change branch names while converting'),
145 _('FILE')),
48 _('FILE')),
146 ('', 'branchsort', None, _('try to sort changesets by branches')),
49 ('', 'branchsort', None, _('try to sort changesets by branches')),
147 ('', 'datesort', None, _('try to sort changesets by date')),
50 ('', 'datesort', None, _('try to sort changesets by date')),
148 ('', 'sourcesort', None, _('preserve source changesets order')),
51 ('', 'sourcesort', None, _('preserve source changesets order')),
149 ('', 'closesort', None, _('try to reorder closed revisions'))],
52 ('', 'closesort', None, _('try to reorder closed revisions'))],
150 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]'),
53 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]'),
151 norepo=True)
54 norepo=True)
152 def convert(ui, src, dest=None, revmapfile=None, **opts):
55 def convert(ui, src, dest=None, revmapfile=None, **opts):
153 """convert a foreign SCM repository to a Mercurial one.
56 """convert a foreign SCM repository to a Mercurial one.
154
57
155 Accepted source formats [identifiers]:
58 Accepted source formats [identifiers]:
156
59
157 - Mercurial [hg]
60 - Mercurial [hg]
158 - CVS [cvs]
61 - CVS [cvs]
159 - Darcs [darcs]
62 - Darcs [darcs]
160 - git [git]
63 - git [git]
161 - Subversion [svn]
64 - Subversion [svn]
162 - Monotone [mtn]
65 - Monotone [mtn]
163 - GNU Arch [gnuarch]
66 - GNU Arch [gnuarch]
164 - Bazaar [bzr]
67 - Bazaar [bzr]
165 - Perforce [p4]
68 - Perforce [p4]
166
69
167 Accepted destination formats [identifiers]:
70 Accepted destination formats [identifiers]:
168
71
169 - Mercurial [hg]
72 - Mercurial [hg]
170 - Subversion [svn] (history on branches is not preserved)
73 - Subversion [svn] (history on branches is not preserved)
171
74
172 If no revision is given, all revisions will be converted.
75 If no revision is given, all revisions will be converted.
173 Otherwise, convert will only import up to the named revision
76 Otherwise, convert will only import up to the named revision
174 (given in a format understood by the source).
77 (given in a format understood by the source).
175
78
176 If no destination directory name is specified, it defaults to the
79 If no destination directory name is specified, it defaults to the
177 basename of the source with ``-hg`` appended. If the destination
80 basename of the source with ``-hg`` appended. If the destination
178 repository doesn't exist, it will be created.
81 repository doesn't exist, it will be created.
179
82
180 By default, all sources except Mercurial will use --branchsort.
83 By default, all sources except Mercurial will use --branchsort.
181 Mercurial uses --sourcesort to preserve original revision numbers
84 Mercurial uses --sourcesort to preserve original revision numbers
182 order. Sort modes have the following effects:
85 order. Sort modes have the following effects:
183
86
184 --branchsort convert from parent to child revision when possible,
87 --branchsort convert from parent to child revision when possible,
185 which means branches are usually converted one after
88 which means branches are usually converted one after
186 the other. It generates more compact repositories.
89 the other. It generates more compact repositories.
187
90
188 --datesort sort revisions by date. Converted repositories have
91 --datesort sort revisions by date. Converted repositories have
189 good-looking changelogs but are often an order of
92 good-looking changelogs but are often an order of
190 magnitude larger than the same ones generated by
93 magnitude larger than the same ones generated by
191 --branchsort.
94 --branchsort.
192
95
193 --sourcesort try to preserve source revisions order, only
96 --sourcesort try to preserve source revisions order, only
194 supported by Mercurial sources.
97 supported by Mercurial sources.
195
98
196 --closesort try to move closed revisions as close as possible
99 --closesort try to move closed revisions as close as possible
197 to parent branches, only supported by Mercurial
100 to parent branches, only supported by Mercurial
198 sources.
101 sources.
199
102
200 If ``REVMAP`` isn't given, it will be put in a default location
103 If ``REVMAP`` isn't given, it will be put in a default location
201 (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
104 (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
202 text file that maps each source commit ID to the destination ID
105 text file that maps each source commit ID to the destination ID
203 for that revision, like so::
106 for that revision, like so::
204
107
205 <source ID> <destination ID>
108 <source ID> <destination ID>
206
109
207 If the file doesn't exist, it's automatically created. It's
110 If the file doesn't exist, it's automatically created. It's
208 updated on each commit copied, so :hg:`convert` can be interrupted
111 updated on each commit copied, so :hg:`convert` can be interrupted
209 and can be run repeatedly to copy new commits.
112 and can be run repeatedly to copy new commits.
210
113
211 The authormap is a simple text file that maps each source commit
114 The authormap is a simple text file that maps each source commit
212 author to a destination commit author. It is handy for source SCMs
115 author to a destination commit author. It is handy for source SCMs
213 that use unix logins to identify authors (e.g.: CVS). One line per
116 that use unix logins to identify authors (e.g.: CVS). One line per
214 author mapping and the line format is::
117 author mapping and the line format is::
215
118
216 source author = destination author
119 source author = destination author
217
120
218 Empty lines and lines starting with a ``#`` are ignored.
121 Empty lines and lines starting with a ``#`` are ignored.
219
122
220 The filemap is a file that allows filtering and remapping of files
123 The filemap is a file that allows filtering and remapping of files
221 and directories. Each line can contain one of the following
124 and directories. Each line can contain one of the following
222 directives::
125 directives::
223
126
224 include path/to/file-or-dir
127 include path/to/file-or-dir
225
128
226 exclude path/to/file-or-dir
129 exclude path/to/file-or-dir
227
130
228 rename path/to/source path/to/destination
131 rename path/to/source path/to/destination
229
132
230 Comment lines start with ``#``. A specified path matches if it
133 Comment lines start with ``#``. A specified path matches if it
231 equals the full relative name of a file or one of its parent
134 equals the full relative name of a file or one of its parent
232 directories. The ``include`` or ``exclude`` directive with the
135 directories. The ``include`` or ``exclude`` directive with the
233 longest matching path applies, so line order does not matter.
136 longest matching path applies, so line order does not matter.
234
137
235 The ``include`` directive causes a file, or all files under a
138 The ``include`` directive causes a file, or all files under a
236 directory, to be included in the destination repository. The default
139 directory, to be included in the destination repository. The default
237 if there are no ``include`` statements is to include everything.
140 if there are no ``include`` statements is to include everything.
238 If there are any ``include`` statements, nothing else is included.
141 If there are any ``include`` statements, nothing else is included.
239 The ``exclude`` directive causes files or directories to
142 The ``exclude`` directive causes files or directories to
240 be omitted. The ``rename`` directive renames a file or directory if
143 be omitted. The ``rename`` directive renames a file or directory if
241 it is converted. To rename from a subdirectory into the root of
144 it is converted. To rename from a subdirectory into the root of
242 the repository, use ``.`` as the path to rename to.
145 the repository, use ``.`` as the path to rename to.
243
146
244 ``--full`` will make sure the converted changesets contain exactly
147 ``--full`` will make sure the converted changesets contain exactly
245 the right files with the right content. It will make a full
148 the right files with the right content. It will make a full
246 conversion of all files, not just the ones that have
149 conversion of all files, not just the ones that have
247 changed. Files that already are correct will not be changed. This
150 changed. Files that already are correct will not be changed. This
248 can be used to apply filemap changes when converting
151 can be used to apply filemap changes when converting
249 incrementally. This is currently only supported for Mercurial and
152 incrementally. This is currently only supported for Mercurial and
250 Subversion.
153 Subversion.
251
154
252 The splicemap is a file that allows insertion of synthetic
155 The splicemap is a file that allows insertion of synthetic
253 history, letting you specify the parents of a revision. This is
156 history, letting you specify the parents of a revision. This is
254 useful if you want to e.g. give a Subversion merge two parents, or
157 useful if you want to e.g. give a Subversion merge two parents, or
255 graft two disconnected series of history together. Each entry
158 graft two disconnected series of history together. Each entry
256 contains a key, followed by a space, followed by one or two
159 contains a key, followed by a space, followed by one or two
257 comma-separated values::
160 comma-separated values::
258
161
259 key parent1, parent2
162 key parent1, parent2
260
163
261 The key is the revision ID in the source
164 The key is the revision ID in the source
262 revision control system whose parents should be modified (same
165 revision control system whose parents should be modified (same
263 format as a key in .hg/shamap). The values are the revision IDs
166 format as a key in .hg/shamap). The values are the revision IDs
264 (in either the source or destination revision control system) that
167 (in either the source or destination revision control system) that
265 should be used as the new parents for that node. For example, if
168 should be used as the new parents for that node. For example, if
266 you have merged "release-1.0" into "trunk", then you should
169 you have merged "release-1.0" into "trunk", then you should
267 specify the revision on "trunk" as the first parent and the one on
170 specify the revision on "trunk" as the first parent and the one on
268 the "release-1.0" branch as the second.
171 the "release-1.0" branch as the second.
269
172
270 The branchmap is a file that allows you to rename a branch when it is
173 The branchmap is a file that allows you to rename a branch when it is
271 being brought in from whatever external repository. When used in
174 being brought in from whatever external repository. When used in
272 conjunction with a splicemap, it allows for a powerful combination
175 conjunction with a splicemap, it allows for a powerful combination
273 to help fix even the most badly mismanaged repositories and turn them
176 to help fix even the most badly mismanaged repositories and turn them
274 into nicely structured Mercurial repositories. The branchmap contains
177 into nicely structured Mercurial repositories. The branchmap contains
275 lines of the form::
178 lines of the form::
276
179
277 original_branch_name new_branch_name
180 original_branch_name new_branch_name
278
181
279 where "original_branch_name" is the name of the branch in the
182 where "original_branch_name" is the name of the branch in the
280 source repository, and "new_branch_name" is the name of the branch
183 source repository, and "new_branch_name" is the name of the branch
281 is the destination repository. No whitespace is allowed in the new
184 is the destination repository. No whitespace is allowed in the new
282 branch name. This can be used to (for instance) move code in one
185 branch name. This can be used to (for instance) move code in one
283 repository from "default" to a named branch.
186 repository from "default" to a named branch.
284
187
285 Mercurial Source
188 Mercurial Source
286 ################
189 ################
287
190
288 The Mercurial source recognizes the following configuration
191 The Mercurial source recognizes the following configuration
289 options, which you can set on the command line with ``--config``:
192 options, which you can set on the command line with ``--config``:
290
193
291 :convert.hg.ignoreerrors: ignore integrity errors when reading.
194 :convert.hg.ignoreerrors: ignore integrity errors when reading.
292 Use it to fix Mercurial repositories with missing revlogs, by
195 Use it to fix Mercurial repositories with missing revlogs, by
293 converting from and to Mercurial. Default is False.
196 converting from and to Mercurial. Default is False.
294
197
295 :convert.hg.saverev: store original revision ID in changeset
198 :convert.hg.saverev: store original revision ID in changeset
296 (forces target IDs to change). It takes a boolean argument and
199 (forces target IDs to change). It takes a boolean argument and
297 defaults to False.
200 defaults to False.
298
201
299 :convert.hg.startrev: specify the initial Mercurial revision.
202 :convert.hg.startrev: specify the initial Mercurial revision.
300 The default is 0.
203 The default is 0.
301
204
302 :convert.hg.revs: revset specifying the source revisions to convert.
205 :convert.hg.revs: revset specifying the source revisions to convert.
303
206
304 CVS Source
207 CVS Source
305 ##########
208 ##########
306
209
307 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
210 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
308 to indicate the starting point of what will be converted. Direct
211 to indicate the starting point of what will be converted. Direct
309 access to the repository files is not needed, unless of course the
212 access to the repository files is not needed, unless of course the
310 repository is ``:local:``. The conversion uses the top level
213 repository is ``:local:``. The conversion uses the top level
311 directory in the sandbox to find the CVS repository, and then uses
214 directory in the sandbox to find the CVS repository, and then uses
312 CVS rlog commands to find files to convert. This means that unless
215 CVS rlog commands to find files to convert. This means that unless
313 a filemap is given, all files under the starting directory will be
216 a filemap is given, all files under the starting directory will be
314 converted, and that any directory reorganization in the CVS
217 converted, and that any directory reorganization in the CVS
315 sandbox is ignored.
218 sandbox is ignored.
316
219
317 The following options can be used with ``--config``:
220 The following options can be used with ``--config``:
318
221
319 :convert.cvsps.cache: Set to False to disable remote log caching,
222 :convert.cvsps.cache: Set to False to disable remote log caching,
320 for testing and debugging purposes. Default is True.
223 for testing and debugging purposes. Default is True.
321
224
322 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
225 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
323 allowed between commits with identical user and log message in
226 allowed between commits with identical user and log message in
324 a single changeset. When very large files were checked in as
227 a single changeset. When very large files were checked in as
325 part of a changeset then the default may not be long enough.
228 part of a changeset then the default may not be long enough.
326 The default is 60.
229 The default is 60.
327
230
328 :convert.cvsps.logencoding: Specify encoding name to be used for
231 :convert.cvsps.logencoding: Specify encoding name to be used for
329 transcoding CVS log messages. Multiple encoding names can be
232 transcoding CVS log messages. Multiple encoding names can be
330 specified as a list (see :hg:`help config.Syntax`), but only
233 specified as a list (see :hg:`help config.Syntax`), but only
331 the first acceptable encoding in the list is used per CVS log
234 the first acceptable encoding in the list is used per CVS log
332 entries. This transcoding is executed before cvslog hook below.
235 entries. This transcoding is executed before cvslog hook below.
333
236
334 :convert.cvsps.mergeto: Specify a regular expression to which
237 :convert.cvsps.mergeto: Specify a regular expression to which
335 commit log messages are matched. If a match occurs, then the
238 commit log messages are matched. If a match occurs, then the
336 conversion process will insert a dummy revision merging the
239 conversion process will insert a dummy revision merging the
337 branch on which this log message occurs to the branch
240 branch on which this log message occurs to the branch
338 indicated in the regex. Default is ``{{mergetobranch
241 indicated in the regex. Default is ``{{mergetobranch
339 ([-\\w]+)}}``
242 ([-\\w]+)}}``
340
243
341 :convert.cvsps.mergefrom: Specify a regular expression to which
244 :convert.cvsps.mergefrom: Specify a regular expression to which
342 commit log messages are matched. If a match occurs, then the
245 commit log messages are matched. If a match occurs, then the
343 conversion process will add the most recent revision on the
246 conversion process will add the most recent revision on the
344 branch indicated in the regex as the second parent of the
247 branch indicated in the regex as the second parent of the
345 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
248 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
346
249
347 :convert.localtimezone: use local time (as determined by the TZ
250 :convert.localtimezone: use local time (as determined by the TZ
348 environment variable) for changeset date/times. The default
251 environment variable) for changeset date/times. The default
349 is False (use UTC).
252 is False (use UTC).
350
253
351 :hooks.cvslog: Specify a Python function to be called at the end of
254 :hooks.cvslog: Specify a Python function to be called at the end of
352 gathering the CVS log. The function is passed a list with the
255 gathering the CVS log. The function is passed a list with the
353 log entries, and can modify the entries in-place, or add or
256 log entries, and can modify the entries in-place, or add or
354 delete them.
257 delete them.
355
258
356 :hooks.cvschangesets: Specify a Python function to be called after
259 :hooks.cvschangesets: Specify a Python function to be called after
357 the changesets are calculated from the CVS log. The
260 the changesets are calculated from the CVS log. The
358 function is passed a list with the changeset entries, and can
261 function is passed a list with the changeset entries, and can
359 modify the changesets in-place, or add or delete them.
262 modify the changesets in-place, or add or delete them.
360
263
361 An additional "debugcvsps" Mercurial command allows the builtin
264 An additional "debugcvsps" Mercurial command allows the builtin
362 changeset merging code to be run without doing a conversion. Its
265 changeset merging code to be run without doing a conversion. Its
363 parameters and output are similar to that of cvsps 2.1. Please see
266 parameters and output are similar to that of cvsps 2.1. Please see
364 the command help for more details.
267 the command help for more details.
365
268
366 Subversion Source
269 Subversion Source
367 #################
270 #################
368
271
369 Subversion source detects classical trunk/branches/tags layouts.
272 Subversion source detects classical trunk/branches/tags layouts.
370 By default, the supplied ``svn://repo/path/`` source URL is
273 By default, the supplied ``svn://repo/path/`` source URL is
371 converted as a single branch. If ``svn://repo/path/trunk`` exists
274 converted as a single branch. If ``svn://repo/path/trunk`` exists
372 it replaces the default branch. If ``svn://repo/path/branches``
275 it replaces the default branch. If ``svn://repo/path/branches``
373 exists, its subdirectories are listed as possible branches. If
276 exists, its subdirectories are listed as possible branches. If
374 ``svn://repo/path/tags`` exists, it is looked for tags referencing
277 ``svn://repo/path/tags`` exists, it is looked for tags referencing
375 converted branches. Default ``trunk``, ``branches`` and ``tags``
278 converted branches. Default ``trunk``, ``branches`` and ``tags``
376 values can be overridden with following options. Set them to paths
279 values can be overridden with following options. Set them to paths
377 relative to the source URL, or leave them blank to disable auto
280 relative to the source URL, or leave them blank to disable auto
378 detection.
281 detection.
379
282
380 The following options can be set with ``--config``:
283 The following options can be set with ``--config``:
381
284
382 :convert.svn.branches: specify the directory containing branches.
285 :convert.svn.branches: specify the directory containing branches.
383 The default is ``branches``.
286 The default is ``branches``.
384
287
385 :convert.svn.tags: specify the directory containing tags. The
288 :convert.svn.tags: specify the directory containing tags. The
386 default is ``tags``.
289 default is ``tags``.
387
290
388 :convert.svn.trunk: specify the name of the trunk branch. The
291 :convert.svn.trunk: specify the name of the trunk branch. The
389 default is ``trunk``.
292 default is ``trunk``.
390
293
391 :convert.localtimezone: use local time (as determined by the TZ
294 :convert.localtimezone: use local time (as determined by the TZ
392 environment variable) for changeset date/times. The default
295 environment variable) for changeset date/times. The default
393 is False (use UTC).
296 is False (use UTC).
394
297
395 Source history can be retrieved starting at a specific revision,
298 Source history can be retrieved starting at a specific revision,
396 instead of being integrally converted. Only single branch
299 instead of being integrally converted. Only single branch
397 conversions are supported.
300 conversions are supported.
398
301
399 :convert.svn.startrev: specify start Subversion revision number.
302 :convert.svn.startrev: specify start Subversion revision number.
400 The default is 0.
303 The default is 0.
401
304
402 Git Source
305 Git Source
403 ##########
306 ##########
404
307
405 The Git importer converts commits from all reachable branches (refs
308 The Git importer converts commits from all reachable branches (refs
406 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
309 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
407 Branches are converted to bookmarks with the same name, with the
310 Branches are converted to bookmarks with the same name, with the
408 leading 'refs/heads' stripped. Git submodules are converted to Git
311 leading 'refs/heads' stripped. Git submodules are converted to Git
409 subrepos in Mercurial.
312 subrepos in Mercurial.
410
313
411 The following options can be set with ``--config``:
314 The following options can be set with ``--config``:
412
315
413 :convert.git.similarity: specify how similar files modified in a
316 :convert.git.similarity: specify how similar files modified in a
414 commit must be to be imported as renames or copies, as a
317 commit must be to be imported as renames or copies, as a
415 percentage between ``0`` (disabled) and ``100`` (files must be
318 percentage between ``0`` (disabled) and ``100`` (files must be
416 identical). For example, ``90`` means that a delete/add pair will
319 identical). For example, ``90`` means that a delete/add pair will
417 be imported as a rename if more than 90% of the file hasn't
320 be imported as a rename if more than 90% of the file hasn't
418 changed. The default is ``50``.
321 changed. The default is ``50``.
419
322
420 :convert.git.findcopiesharder: while detecting copies, look at all
323 :convert.git.findcopiesharder: while detecting copies, look at all
421 files in the working copy instead of just changed ones. This
324 files in the working copy instead of just changed ones. This
422 is very expensive for large projects, and is only effective when
325 is very expensive for large projects, and is only effective when
423 ``convert.git.similarity`` is greater than 0. The default is False.
326 ``convert.git.similarity`` is greater than 0. The default is False.
424
327
425 :convert.git.renamelimit: perform rename and copy detection up to this
328 :convert.git.renamelimit: perform rename and copy detection up to this
426 many changed files in a commit. Increasing this will make rename
329 many changed files in a commit. Increasing this will make rename
427 and copy detection more accurate but will significantly slow down
330 and copy detection more accurate but will significantly slow down
428 computation on large projects. The option is only relevant if
331 computation on large projects. The option is only relevant if
429 ``convert.git.similarity`` is greater than 0. The default is
332 ``convert.git.similarity`` is greater than 0. The default is
430 ``400``.
333 ``400``.
431
334
432 :convert.git.committeractions: list of actions to take when processing
335 :convert.git.committeractions: list of actions to take when processing
433 author and committer values.
336 author and committer values.
434
337
435 Git commits have separate author (who wrote the commit) and committer
338 Git commits have separate author (who wrote the commit) and committer
436 (who applied the commit) fields. Not all destinations support separate
339 (who applied the commit) fields. Not all destinations support separate
437 author and committer fields (including Mercurial). This config option
340 author and committer fields (including Mercurial). This config option
438 controls what to do with these author and committer fields during
341 controls what to do with these author and committer fields during
439 conversion.
342 conversion.
440
343
441 A value of ``messagedifferent`` will append a ``committer: ...``
344 A value of ``messagedifferent`` will append a ``committer: ...``
442 line to the commit message if the Git committer is different from the
345 line to the commit message if the Git committer is different from the
443 author. The prefix of that line can be specified using the syntax
346 author. The prefix of that line can be specified using the syntax
444 ``messagedifferent=<prefix>``. e.g. ``messagedifferent=git-committer:``.
347 ``messagedifferent=<prefix>``. e.g. ``messagedifferent=git-committer:``.
445 When a prefix is specified, a space will always be inserted between the
348 When a prefix is specified, a space will always be inserted between the
446 prefix and the value.
349 prefix and the value.
447
350
448 ``messagealways`` behaves like ``messagedifferent`` except it will
351 ``messagealways`` behaves like ``messagedifferent`` except it will
449 always result in a ``committer: ...`` line being appended to the commit
352 always result in a ``committer: ...`` line being appended to the commit
450 message. This value is mutually exclusive with ``messagedifferent``.
353 message. This value is mutually exclusive with ``messagedifferent``.
451
354
452 ``dropcommitter`` will remove references to the committer. Only
355 ``dropcommitter`` will remove references to the committer. Only
453 references to the author will remain. Actions that add references
356 references to the author will remain. Actions that add references
454 to the committer will have no effect when this is set.
357 to the committer will have no effect when this is set.
455
358
456 ``replaceauthor`` will replace the value of the author field with
359 ``replaceauthor`` will replace the value of the author field with
457 the committer. Other actions that add references to the committer
360 the committer. Other actions that add references to the committer
458 will still take effect when this is set.
361 will still take effect when this is set.
459
362
460 The default is ``messagedifferent``.
363 The default is ``messagedifferent``.
461
364
462 :convert.git.extrakeys: list of extra keys from commit metadata to copy to
365 :convert.git.extrakeys: list of extra keys from commit metadata to copy to
463 the destination. Some Git repositories store extra metadata in commits.
366 the destination. Some Git repositories store extra metadata in commits.
464 By default, this non-default metadata will be lost during conversion.
367 By default, this non-default metadata will be lost during conversion.
465 Setting this config option can retain that metadata. Some built-in
368 Setting this config option can retain that metadata. Some built-in
466 keys such as ``parent`` and ``branch`` are not allowed to be copied.
369 keys such as ``parent`` and ``branch`` are not allowed to be copied.
467
370
468 :convert.git.remoteprefix: remote refs are converted as bookmarks with
371 :convert.git.remoteprefix: remote refs are converted as bookmarks with
469 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
372 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
470 is 'remote'.
373 is 'remote'.
471
374
472 :convert.git.saverev: whether to store the original Git commit ID in the
375 :convert.git.saverev: whether to store the original Git commit ID in the
473 metadata of the destination commit. The default is True.
376 metadata of the destination commit. The default is True.
474
377
475 :convert.git.skipsubmodules: does not convert root level .gitmodules files
378 :convert.git.skipsubmodules: does not convert root level .gitmodules files
476 or files with 160000 mode indicating a submodule. Default is False.
379 or files with 160000 mode indicating a submodule. Default is False.
477
380
478 Perforce Source
381 Perforce Source
479 ###############
382 ###############
480
383
481 The Perforce (P4) importer can be given a p4 depot path or a
384 The Perforce (P4) importer can be given a p4 depot path or a
482 client specification as source. It will convert all files in the
385 client specification as source. It will convert all files in the
483 source to a flat Mercurial repository, ignoring labels, branches
386 source to a flat Mercurial repository, ignoring labels, branches
484 and integrations. Note that when a depot path is given you then
387 and integrations. Note that when a depot path is given you then
485 usually should specify a target directory, because otherwise the
388 usually should specify a target directory, because otherwise the
486 target may be named ``...-hg``.
389 target may be named ``...-hg``.
487
390
488 The following options can be set with ``--config``:
391 The following options can be set with ``--config``:
489
392
490 :convert.p4.encoding: specify the encoding to use when decoding standard
393 :convert.p4.encoding: specify the encoding to use when decoding standard
491 output of the Perforce command line tool. The default is default system
394 output of the Perforce command line tool. The default is default system
492 encoding.
395 encoding.
493
396
494 :convert.p4.startrev: specify initial Perforce revision (a
397 :convert.p4.startrev: specify initial Perforce revision (a
495 Perforce changelist number).
398 Perforce changelist number).
496
399
497 Mercurial Destination
400 Mercurial Destination
498 #####################
401 #####################
499
402
500 The Mercurial destination will recognize Mercurial subrepositories in the
403 The Mercurial destination will recognize Mercurial subrepositories in the
501 destination directory, and update the .hgsubstate file automatically if the
404 destination directory, and update the .hgsubstate file automatically if the
502 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
405 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
503 Converting a repository with subrepositories requires converting a single
406 Converting a repository with subrepositories requires converting a single
504 repository at a time, from the bottom up.
407 repository at a time, from the bottom up.
505
408
506 .. container:: verbose
409 .. container:: verbose
507
410
508 An example showing how to convert a repository with subrepositories::
411 An example showing how to convert a repository with subrepositories::
509
412
510 # so convert knows the type when it sees a non empty destination
413 # so convert knows the type when it sees a non empty destination
511 $ hg init converted
414 $ hg init converted
512
415
513 $ hg convert orig/sub1 converted/sub1
416 $ hg convert orig/sub1 converted/sub1
514 $ hg convert orig/sub2 converted/sub2
417 $ hg convert orig/sub2 converted/sub2
515 $ hg convert orig converted
418 $ hg convert orig converted
516
419
517 The following options are supported:
420 The following options are supported:
518
421
519 :convert.hg.clonebranches: dispatch source branches in separate
422 :convert.hg.clonebranches: dispatch source branches in separate
520 clones. The default is False.
423 clones. The default is False.
521
424
522 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
425 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
523 ``default``.
426 ``default``.
524
427
525 :convert.hg.usebranchnames: preserve branch names. The default is
428 :convert.hg.usebranchnames: preserve branch names. The default is
526 True.
429 True.
527
430
528 :convert.hg.sourcename: records the given string as a 'convert_source' extra
431 :convert.hg.sourcename: records the given string as a 'convert_source' extra
529 value on each commit made in the target repository. The default is None.
432 value on each commit made in the target repository. The default is None.
530
433
531 All Destinations
434 All Destinations
532 ################
435 ################
533
436
534 All destination types accept the following options:
437 All destination types accept the following options:
535
438
536 :convert.skiptags: does not convert tags from the source repo to the target
439 :convert.skiptags: does not convert tags from the source repo to the target
537 repo. The default is False.
440 repo. The default is False.
538 """
441 """
539 return convcmd.convert(ui, src, dest, revmapfile, **opts)
442 return convcmd.convert(ui, src, dest, revmapfile, **opts)
540
443
541 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
444 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
542 def debugsvnlog(ui, **opts):
445 def debugsvnlog(ui, **opts):
543 return subversion.debugsvnlog(ui, **opts)
446 return subversion.debugsvnlog(ui, **opts)
544
447
545 @command('debugcvsps',
448 @command('debugcvsps',
546 [
449 [
547 # Main options shared with cvsps-2.1
450 # Main options shared with cvsps-2.1
548 ('b', 'branches', [], _('only return changes on specified branches')),
451 ('b', 'branches', [], _('only return changes on specified branches')),
549 ('p', 'prefix', '', _('prefix to remove from file names')),
452 ('p', 'prefix', '', _('prefix to remove from file names')),
550 ('r', 'revisions', [],
453 ('r', 'revisions', [],
551 _('only return changes after or between specified tags')),
454 _('only return changes after or between specified tags')),
552 ('u', 'update-cache', None, _("update cvs log cache")),
455 ('u', 'update-cache', None, _("update cvs log cache")),
553 ('x', 'new-cache', None, _("create new cvs log cache")),
456 ('x', 'new-cache', None, _("create new cvs log cache")),
554 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
457 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
555 ('', 'root', '', _('specify cvsroot')),
458 ('', 'root', '', _('specify cvsroot')),
556 # Options specific to builtin cvsps
459 # Options specific to builtin cvsps
557 ('', 'parents', '', _('show parent changesets')),
460 ('', 'parents', '', _('show parent changesets')),
558 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
461 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
559 # Options that are ignored for compatibility with cvsps-2.1
462 # Options that are ignored for compatibility with cvsps-2.1
560 ('A', 'cvs-direct', None, _('ignored for compatibility')),
463 ('A', 'cvs-direct', None, _('ignored for compatibility')),
561 ],
464 ],
562 _('hg debugcvsps [OPTION]... [PATH]...'),
465 _('hg debugcvsps [OPTION]... [PATH]...'),
563 norepo=True)
466 norepo=True)
564 def debugcvsps(ui, *args, **opts):
467 def debugcvsps(ui, *args, **opts):
565 '''create changeset information from CVS
468 '''create changeset information from CVS
566
469
567 This command is intended as a debugging tool for the CVS to
470 This command is intended as a debugging tool for the CVS to
568 Mercurial converter, and can be used as a direct replacement for
471 Mercurial converter, and can be used as a direct replacement for
569 cvsps.
472 cvsps.
570
473
571 Hg debugcvsps reads the CVS rlog for current directory (or any
474 Hg debugcvsps reads the CVS rlog for current directory (or any
572 named directory) in the CVS repository, and converts the log to a
475 named directory) in the CVS repository, and converts the log to a
573 series of changesets based on matching commit log entries and
476 series of changesets based on matching commit log entries and
574 dates.'''
477 dates.'''
575 return cvsps.debugcvsps(ui, *args, **opts)
478 return cvsps.debugcvsps(ui, *args, **opts)
576
479
577 def kwconverted(ctx, name):
480 def kwconverted(ctx, name):
578 rev = ctx.extra().get('convert_revision', '')
481 rev = ctx.extra().get('convert_revision', '')
579 if rev.startswith('svn:'):
482 if rev.startswith('svn:'):
580 if name == 'svnrev':
483 if name == 'svnrev':
581 return str(subversion.revsplit(rev)[2])
484 return str(subversion.revsplit(rev)[2])
582 elif name == 'svnpath':
485 elif name == 'svnpath':
583 return subversion.revsplit(rev)[1]
486 return subversion.revsplit(rev)[1]
584 elif name == 'svnuuid':
487 elif name == 'svnuuid':
585 return subversion.revsplit(rev)[0]
488 return subversion.revsplit(rev)[0]
586 return rev
489 return rev
587
490
588 templatekeyword = registrar.templatekeyword()
491 templatekeyword = registrar.templatekeyword()
589
492
590 @templatekeyword('svnrev')
493 @templatekeyword('svnrev')
591 def kwsvnrev(repo, ctx, **args):
494 def kwsvnrev(repo, ctx, **args):
592 """String. Converted subversion revision number."""
495 """String. Converted subversion revision number."""
593 return kwconverted(ctx, 'svnrev')
496 return kwconverted(ctx, 'svnrev')
594
497
595 @templatekeyword('svnpath')
498 @templatekeyword('svnpath')
596 def kwsvnpath(repo, ctx, **args):
499 def kwsvnpath(repo, ctx, **args):
597 """String. Converted subversion revision project path."""
500 """String. Converted subversion revision project path."""
598 return kwconverted(ctx, 'svnpath')
501 return kwconverted(ctx, 'svnpath')
599
502
600 @templatekeyword('svnuuid')
503 @templatekeyword('svnuuid')
601 def kwsvnuuid(repo, ctx, **args):
504 def kwsvnuuid(repo, ctx, **args):
602 """String. Converted subversion revision repository identifier."""
505 """String. Converted subversion revision repository identifier."""
603 return kwconverted(ctx, 'svnuuid')
506 return kwconverted(ctx, 'svnuuid')
604
507
605 # tell hggettext to extract docstrings from these functions:
508 # tell hggettext to extract docstrings from these functions:
606 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
509 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
@@ -1,369 +1,373 b''
1 # Perforce source for convert extension.
1 # Perforce source for convert extension.
2 #
2 #
3 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
3 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
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 marshal
9 import marshal
10 import re
10 import re
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13 from mercurial import (
13 from mercurial import (
14 error,
14 error,
15 util,
15 util,
16 )
16 )
17
17
18 from . import common
18 from . import common
19
19
20 def loaditer(f):
20 def loaditer(f):
21 "Yield the dictionary objects generated by p4"
21 "Yield the dictionary objects generated by p4"
22 try:
22 try:
23 while True:
23 while True:
24 d = marshal.load(f)
24 d = marshal.load(f)
25 if not d:
25 if not d:
26 break
26 break
27 yield d
27 yield d
28 except EOFError:
28 except EOFError:
29 pass
29 pass
30
30
31 def decodefilename(filename):
31 def decodefilename(filename):
32 """Perforce escapes special characters @, #, *, or %
32 """Perforce escapes special characters @, #, *, or %
33 with %40, %23, %2A, or %25 respectively
33 with %40, %23, %2A, or %25 respectively
34
34
35 >>> decodefilename(b'portable-net45%252Bnetcore45%252Bwp8%252BMonoAndroid')
35 >>> decodefilename(b'portable-net45%252Bnetcore45%252Bwp8%252BMonoAndroid')
36 'portable-net45%2Bnetcore45%2Bwp8%2BMonoAndroid'
36 'portable-net45%2Bnetcore45%2Bwp8%2BMonoAndroid'
37 >>> decodefilename(b'//Depot/Directory/%2525/%2523/%23%40.%2A')
37 >>> decodefilename(b'//Depot/Directory/%2525/%2523/%23%40.%2A')
38 '//Depot/Directory/%25/%23/#@.*'
38 '//Depot/Directory/%25/%23/#@.*'
39 """
39 """
40 replacements = [('%2A', '*'), ('%23', '#'), ('%40', '@'), ('%25', '%')]
40 replacements = [('%2A', '*'), ('%23', '#'), ('%40', '@'), ('%25', '%')]
41 for k, v in replacements:
41 for k, v in replacements:
42 filename = filename.replace(k, v)
42 filename = filename.replace(k, v)
43 return filename
43 return filename
44
44
45 class p4_source(common.converter_source):
45 class p4_source(common.converter_source):
46 def __init__(self, ui, path, revs=None):
46 def __init__(self, ui, path, revs=None):
47 # avoid import cycle
48 from . import convcmd
49
47 super(p4_source, self).__init__(ui, path, revs=revs)
50 super(p4_source, self).__init__(ui, path, revs=revs)
48
51
49 if "/" in path and not path.startswith('//'):
52 if "/" in path and not path.startswith('//'):
50 raise common.NoRepo(_('%s does not look like a P4 repository') %
53 raise common.NoRepo(_('%s does not look like a P4 repository') %
51 path)
54 path)
52
55
53 common.checktool('p4', abort=False)
56 common.checktool('p4', abort=False)
54
57
55 self.revmap = {}
58 self.revmap = {}
56 self.encoding = self.ui.config('convert', 'p4.encoding')
59 self.encoding = self.ui.config('convert', 'p4.encoding',
60 convcmd.orig_encoding)
57 self.re_type = re.compile(
61 self.re_type = re.compile(
58 "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
62 "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
59 "(\+\w+)?$")
63 "(\+\w+)?$")
60 self.re_keywords = re.compile(
64 self.re_keywords = re.compile(
61 r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
65 r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
62 r":[^$\n]*\$")
66 r":[^$\n]*\$")
63 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
67 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
64
68
65 if revs and len(revs) > 1:
69 if revs and len(revs) > 1:
66 raise error.Abort(_("p4 source does not support specifying "
70 raise error.Abort(_("p4 source does not support specifying "
67 "multiple revisions"))
71 "multiple revisions"))
68
72
69 def setrevmap(self, revmap):
73 def setrevmap(self, revmap):
70 """Sets the parsed revmap dictionary.
74 """Sets the parsed revmap dictionary.
71
75
72 Revmap stores mappings from a source revision to a target revision.
76 Revmap stores mappings from a source revision to a target revision.
73 It is set in convertcmd.convert and provided by the user as a file
77 It is set in convertcmd.convert and provided by the user as a file
74 on the commandline.
78 on the commandline.
75
79
76 Revisions in the map are considered beeing present in the
80 Revisions in the map are considered beeing present in the
77 repository and ignored during _parse(). This allows for incremental
81 repository and ignored during _parse(). This allows for incremental
78 imports if a revmap is provided.
82 imports if a revmap is provided.
79 """
83 """
80 self.revmap = revmap
84 self.revmap = revmap
81
85
82 def _parse_view(self, path):
86 def _parse_view(self, path):
83 "Read changes affecting the path"
87 "Read changes affecting the path"
84 cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
88 cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
85 stdout = util.popen(cmd, mode='rb')
89 stdout = util.popen(cmd, mode='rb')
86 p4changes = {}
90 p4changes = {}
87 for d in loaditer(stdout):
91 for d in loaditer(stdout):
88 c = d.get("change", None)
92 c = d.get("change", None)
89 if c:
93 if c:
90 p4changes[c] = True
94 p4changes[c] = True
91 return p4changes
95 return p4changes
92
96
93 def _parse(self, ui, path):
97 def _parse(self, ui, path):
94 "Prepare list of P4 filenames and revisions to import"
98 "Prepare list of P4 filenames and revisions to import"
95 p4changes = {}
99 p4changes = {}
96 changeset = {}
100 changeset = {}
97 files_map = {}
101 files_map = {}
98 copies_map = {}
102 copies_map = {}
99 localname = {}
103 localname = {}
100 depotname = {}
104 depotname = {}
101 heads = []
105 heads = []
102
106
103 ui.status(_('reading p4 views\n'))
107 ui.status(_('reading p4 views\n'))
104
108
105 # read client spec or view
109 # read client spec or view
106 if "/" in path:
110 if "/" in path:
107 p4changes.update(self._parse_view(path))
111 p4changes.update(self._parse_view(path))
108 if path.startswith("//") and path.endswith("/..."):
112 if path.startswith("//") and path.endswith("/..."):
109 views = {path[:-3]:""}
113 views = {path[:-3]:""}
110 else:
114 else:
111 views = {"//": ""}
115 views = {"//": ""}
112 else:
116 else:
113 cmd = 'p4 -G client -o %s' % util.shellquote(path)
117 cmd = 'p4 -G client -o %s' % util.shellquote(path)
114 clientspec = marshal.load(util.popen(cmd, mode='rb'))
118 clientspec = marshal.load(util.popen(cmd, mode='rb'))
115
119
116 views = {}
120 views = {}
117 for client in clientspec:
121 for client in clientspec:
118 if client.startswith("View"):
122 if client.startswith("View"):
119 sview, cview = clientspec[client].split()
123 sview, cview = clientspec[client].split()
120 p4changes.update(self._parse_view(sview))
124 p4changes.update(self._parse_view(sview))
121 if sview.endswith("...") and cview.endswith("..."):
125 if sview.endswith("...") and cview.endswith("..."):
122 sview = sview[:-3]
126 sview = sview[:-3]
123 cview = cview[:-3]
127 cview = cview[:-3]
124 cview = cview[2:]
128 cview = cview[2:]
125 cview = cview[cview.find("/") + 1:]
129 cview = cview[cview.find("/") + 1:]
126 views[sview] = cview
130 views[sview] = cview
127
131
128 # list of changes that affect our source files
132 # list of changes that affect our source files
129 p4changes = p4changes.keys()
133 p4changes = p4changes.keys()
130 p4changes.sort(key=int)
134 p4changes.sort(key=int)
131
135
132 # list with depot pathnames, longest first
136 # list with depot pathnames, longest first
133 vieworder = views.keys()
137 vieworder = views.keys()
134 vieworder.sort(key=len, reverse=True)
138 vieworder.sort(key=len, reverse=True)
135
139
136 # handle revision limiting
140 # handle revision limiting
137 startrev = self.ui.config('convert', 'p4.startrev')
141 startrev = self.ui.config('convert', 'p4.startrev')
138
142
139 # now read the full changelists to get the list of file revisions
143 # now read the full changelists to get the list of file revisions
140 ui.status(_('collecting p4 changelists\n'))
144 ui.status(_('collecting p4 changelists\n'))
141 lastid = None
145 lastid = None
142 for change in p4changes:
146 for change in p4changes:
143 if startrev and int(change) < int(startrev):
147 if startrev and int(change) < int(startrev):
144 continue
148 continue
145 if self.revs and int(change) > int(self.revs[0]):
149 if self.revs and int(change) > int(self.revs[0]):
146 continue
150 continue
147 if change in self.revmap:
151 if change in self.revmap:
148 # Ignore already present revisions, but set the parent pointer.
152 # Ignore already present revisions, but set the parent pointer.
149 lastid = change
153 lastid = change
150 continue
154 continue
151
155
152 if lastid:
156 if lastid:
153 parents = [lastid]
157 parents = [lastid]
154 else:
158 else:
155 parents = []
159 parents = []
156
160
157 d = self._fetch_revision(change)
161 d = self._fetch_revision(change)
158 c = self._construct_commit(d, parents)
162 c = self._construct_commit(d, parents)
159
163
160 descarr = c.desc.splitlines(True)
164 descarr = c.desc.splitlines(True)
161 if len(descarr) > 0:
165 if len(descarr) > 0:
162 shortdesc = descarr[0].rstrip('\r\n')
166 shortdesc = descarr[0].rstrip('\r\n')
163 else:
167 else:
164 shortdesc = '**empty changelist description**'
168 shortdesc = '**empty changelist description**'
165
169
166 t = '%s %s' % (c.rev, repr(shortdesc)[1:-1])
170 t = '%s %s' % (c.rev, repr(shortdesc)[1:-1])
167 ui.status(util.ellipsis(t, 80) + '\n')
171 ui.status(util.ellipsis(t, 80) + '\n')
168
172
169 files = []
173 files = []
170 copies = {}
174 copies = {}
171 copiedfiles = []
175 copiedfiles = []
172 i = 0
176 i = 0
173 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
177 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
174 oldname = d["depotFile%d" % i]
178 oldname = d["depotFile%d" % i]
175 filename = None
179 filename = None
176 for v in vieworder:
180 for v in vieworder:
177 if oldname.lower().startswith(v.lower()):
181 if oldname.lower().startswith(v.lower()):
178 filename = decodefilename(views[v] + oldname[len(v):])
182 filename = decodefilename(views[v] + oldname[len(v):])
179 break
183 break
180 if filename:
184 if filename:
181 files.append((filename, d["rev%d" % i]))
185 files.append((filename, d["rev%d" % i]))
182 depotname[filename] = oldname
186 depotname[filename] = oldname
183 if (d.get("action%d" % i) == "move/add"):
187 if (d.get("action%d" % i) == "move/add"):
184 copiedfiles.append(filename)
188 copiedfiles.append(filename)
185 localname[oldname] = filename
189 localname[oldname] = filename
186 i += 1
190 i += 1
187
191
188 # Collect information about copied files
192 # Collect information about copied files
189 for filename in copiedfiles:
193 for filename in copiedfiles:
190 oldname = depotname[filename]
194 oldname = depotname[filename]
191
195
192 flcmd = 'p4 -G filelog %s' \
196 flcmd = 'p4 -G filelog %s' \
193 % util.shellquote(oldname)
197 % util.shellquote(oldname)
194 flstdout = util.popen(flcmd, mode='rb')
198 flstdout = util.popen(flcmd, mode='rb')
195
199
196 copiedfilename = None
200 copiedfilename = None
197 for d in loaditer(flstdout):
201 for d in loaditer(flstdout):
198 copiedoldname = None
202 copiedoldname = None
199
203
200 i = 0
204 i = 0
201 while ("change%d" % i) in d:
205 while ("change%d" % i) in d:
202 if (d["change%d" % i] == change and
206 if (d["change%d" % i] == change and
203 d["action%d" % i] == "move/add"):
207 d["action%d" % i] == "move/add"):
204 j = 0
208 j = 0
205 while ("file%d,%d" % (i, j)) in d:
209 while ("file%d,%d" % (i, j)) in d:
206 if d["how%d,%d" % (i, j)] == "moved from":
210 if d["how%d,%d" % (i, j)] == "moved from":
207 copiedoldname = d["file%d,%d" % (i, j)]
211 copiedoldname = d["file%d,%d" % (i, j)]
208 break
212 break
209 j += 1
213 j += 1
210 i += 1
214 i += 1
211
215
212 if copiedoldname and copiedoldname in localname:
216 if copiedoldname and copiedoldname in localname:
213 copiedfilename = localname[copiedoldname]
217 copiedfilename = localname[copiedoldname]
214 break
218 break
215
219
216 if copiedfilename:
220 if copiedfilename:
217 copies[filename] = copiedfilename
221 copies[filename] = copiedfilename
218 else:
222 else:
219 ui.warn(_("cannot find source for copied file: %s@%s\n")
223 ui.warn(_("cannot find source for copied file: %s@%s\n")
220 % (filename, change))
224 % (filename, change))
221
225
222 changeset[change] = c
226 changeset[change] = c
223 files_map[change] = files
227 files_map[change] = files
224 copies_map[change] = copies
228 copies_map[change] = copies
225 lastid = change
229 lastid = change
226
230
227 if lastid and len(changeset) > 0:
231 if lastid and len(changeset) > 0:
228 heads = [lastid]
232 heads = [lastid]
229
233
230 return {
234 return {
231 'changeset': changeset,
235 'changeset': changeset,
232 'files': files_map,
236 'files': files_map,
233 'copies': copies_map,
237 'copies': copies_map,
234 'heads': heads,
238 'heads': heads,
235 'depotname': depotname,
239 'depotname': depotname,
236 }
240 }
237
241
238 @util.propertycache
242 @util.propertycache
239 def _parse_once(self):
243 def _parse_once(self):
240 return self._parse(self.ui, self.path)
244 return self._parse(self.ui, self.path)
241
245
242 @util.propertycache
246 @util.propertycache
243 def copies(self):
247 def copies(self):
244 return self._parse_once['copies']
248 return self._parse_once['copies']
245
249
246 @util.propertycache
250 @util.propertycache
247 def files(self):
251 def files(self):
248 return self._parse_once['files']
252 return self._parse_once['files']
249
253
250 @util.propertycache
254 @util.propertycache
251 def changeset(self):
255 def changeset(self):
252 return self._parse_once['changeset']
256 return self._parse_once['changeset']
253
257
254 @util.propertycache
258 @util.propertycache
255 def heads(self):
259 def heads(self):
256 return self._parse_once['heads']
260 return self._parse_once['heads']
257
261
258 @util.propertycache
262 @util.propertycache
259 def depotname(self):
263 def depotname(self):
260 return self._parse_once['depotname']
264 return self._parse_once['depotname']
261
265
262 def getheads(self):
266 def getheads(self):
263 return self.heads
267 return self.heads
264
268
265 def getfile(self, name, rev):
269 def getfile(self, name, rev):
266 cmd = 'p4 -G print %s' \
270 cmd = 'p4 -G print %s' \
267 % util.shellquote("%s#%s" % (self.depotname[name], rev))
271 % util.shellquote("%s#%s" % (self.depotname[name], rev))
268
272
269 lasterror = None
273 lasterror = None
270 while True:
274 while True:
271 stdout = util.popen(cmd, mode='rb')
275 stdout = util.popen(cmd, mode='rb')
272
276
273 mode = None
277 mode = None
274 contents = []
278 contents = []
275 keywords = None
279 keywords = None
276
280
277 for d in loaditer(stdout):
281 for d in loaditer(stdout):
278 code = d["code"]
282 code = d["code"]
279 data = d.get("data")
283 data = d.get("data")
280
284
281 if code == "error":
285 if code == "error":
282 # if this is the first time error happened
286 # if this is the first time error happened
283 # re-attempt getting the file
287 # re-attempt getting the file
284 if not lasterror:
288 if not lasterror:
285 lasterror = IOError(d["generic"], data)
289 lasterror = IOError(d["generic"], data)
286 # this will exit inner-most for-loop
290 # this will exit inner-most for-loop
287 break
291 break
288 else:
292 else:
289 raise lasterror
293 raise lasterror
290
294
291 elif code == "stat":
295 elif code == "stat":
292 action = d.get("action")
296 action = d.get("action")
293 if action in ["purge", "delete", "move/delete"]:
297 if action in ["purge", "delete", "move/delete"]:
294 return None, None
298 return None, None
295 p4type = self.re_type.match(d["type"])
299 p4type = self.re_type.match(d["type"])
296 if p4type:
300 if p4type:
297 mode = ""
301 mode = ""
298 flags = ((p4type.group(1) or "")
302 flags = ((p4type.group(1) or "")
299 + (p4type.group(3) or ""))
303 + (p4type.group(3) or ""))
300 if "x" in flags:
304 if "x" in flags:
301 mode = "x"
305 mode = "x"
302 if p4type.group(2) == "symlink":
306 if p4type.group(2) == "symlink":
303 mode = "l"
307 mode = "l"
304 if "ko" in flags:
308 if "ko" in flags:
305 keywords = self.re_keywords_old
309 keywords = self.re_keywords_old
306 elif "k" in flags:
310 elif "k" in flags:
307 keywords = self.re_keywords
311 keywords = self.re_keywords
308
312
309 elif code == "text" or code == "binary":
313 elif code == "text" or code == "binary":
310 contents.append(data)
314 contents.append(data)
311
315
312 lasterror = None
316 lasterror = None
313
317
314 if not lasterror:
318 if not lasterror:
315 break
319 break
316
320
317 if mode is None:
321 if mode is None:
318 return None, None
322 return None, None
319
323
320 contents = ''.join(contents)
324 contents = ''.join(contents)
321
325
322 if keywords:
326 if keywords:
323 contents = keywords.sub("$\\1$", contents)
327 contents = keywords.sub("$\\1$", contents)
324 if mode == "l" and contents.endswith("\n"):
328 if mode == "l" and contents.endswith("\n"):
325 contents = contents[:-1]
329 contents = contents[:-1]
326
330
327 return contents, mode
331 return contents, mode
328
332
329 def getchanges(self, rev, full):
333 def getchanges(self, rev, full):
330 if full:
334 if full:
331 raise error.Abort(_("convert from p4 does not support --full"))
335 raise error.Abort(_("convert from p4 does not support --full"))
332 return self.files[rev], self.copies[rev], set()
336 return self.files[rev], self.copies[rev], set()
333
337
334 def _construct_commit(self, obj, parents=None):
338 def _construct_commit(self, obj, parents=None):
335 """
339 """
336 Constructs a common.commit object from an unmarshalled
340 Constructs a common.commit object from an unmarshalled
337 `p4 describe` output
341 `p4 describe` output
338 """
342 """
339 desc = self.recode(obj.get("desc", ""))
343 desc = self.recode(obj.get("desc", ""))
340 date = (int(obj["time"]), 0) # timezone not set
344 date = (int(obj["time"]), 0) # timezone not set
341 if parents is None:
345 if parents is None:
342 parents = []
346 parents = []
343
347
344 return common.commit(author=self.recode(obj["user"]),
348 return common.commit(author=self.recode(obj["user"]),
345 date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
349 date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
346 parents=parents, desc=desc, branch=None, rev=obj['change'],
350 parents=parents, desc=desc, branch=None, rev=obj['change'],
347 extra={"p4": obj['change'], "convert_revision": obj['change']})
351 extra={"p4": obj['change'], "convert_revision": obj['change']})
348
352
349 def _fetch_revision(self, rev):
353 def _fetch_revision(self, rev):
350 """Return an output of `p4 describe` including author, commit date as
354 """Return an output of `p4 describe` including author, commit date as
351 a dictionary."""
355 a dictionary."""
352 cmd = "p4 -G describe -s %s" % rev
356 cmd = "p4 -G describe -s %s" % rev
353 stdout = util.popen(cmd, mode='rb')
357 stdout = util.popen(cmd, mode='rb')
354 return marshal.load(stdout)
358 return marshal.load(stdout)
355
359
356 def getcommit(self, rev):
360 def getcommit(self, rev):
357 if rev in self.changeset:
361 if rev in self.changeset:
358 return self.changeset[rev]
362 return self.changeset[rev]
359 elif rev in self.revmap:
363 elif rev in self.revmap:
360 d = self._fetch_revision(rev)
364 d = self._fetch_revision(rev)
361 return self._construct_commit(d, parents=None)
365 return self._construct_commit(d, parents=None)
362 raise error.Abort(
366 raise error.Abort(
363 _("cannot find %s in the revmap or parsed changesets") % rev)
367 _("cannot find %s in the revmap or parsed changesets") % rev)
364
368
365 def gettags(self):
369 def gettags(self):
366 return {}
370 return {}
367
371
368 def getchangedfiles(self, rev, i):
372 def getchangedfiles(self, rev, i):
369 return sorted([x[0] for x in self.files[rev]])
373 return sorted([x[0] for x in self.files[rev]])
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now