##// END OF EJS Templates
convert: add config for recording the source name...
Durham Goode -
r25750:c9093d4d default
parent child Browse files
Show More
@@ -1,434 +1,437 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 import convcmd
10 import convcmd
11 import cvsps
11 import cvsps
12 import subversion
12 import subversion
13 from mercurial import cmdutil, templatekw
13 from mercurial import cmdutil, templatekw
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15
15
16 cmdtable = {}
16 cmdtable = {}
17 command = cmdutil.command(cmdtable)
17 command = cmdutil.command(cmdtable)
18 # Note for extension authors: ONLY specify testedwith = 'internal' for
18 # Note for extension authors: ONLY specify testedwith = 'internal' for
19 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
19 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
20 # be specifying the version(s) of Mercurial they are tested with, or
20 # be specifying the version(s) of Mercurial they are tested with, or
21 # leave the attribute unspecified.
21 # leave the attribute unspecified.
22 testedwith = 'internal'
22 testedwith = 'internal'
23
23
24 # Commands definition was moved elsewhere to ease demandload job.
24 # Commands definition was moved elsewhere to ease demandload job.
25
25
26 @command('convert',
26 @command('convert',
27 [('', 'authors', '',
27 [('', 'authors', '',
28 _('username mapping filename (DEPRECATED, use --authormap instead)'),
28 _('username mapping filename (DEPRECATED, use --authormap instead)'),
29 _('FILE')),
29 _('FILE')),
30 ('s', 'source-type', '', _('source repository type'), _('TYPE')),
30 ('s', 'source-type', '', _('source repository type'), _('TYPE')),
31 ('d', 'dest-type', '', _('destination repository type'), _('TYPE')),
31 ('d', 'dest-type', '', _('destination repository type'), _('TYPE')),
32 ('r', 'rev', [], _('import up to source revision REV'), _('REV')),
32 ('r', 'rev', [], _('import up to source revision REV'), _('REV')),
33 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
33 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
34 ('', 'filemap', '', _('remap file names using contents of file'),
34 ('', 'filemap', '', _('remap file names using contents of file'),
35 _('FILE')),
35 _('FILE')),
36 ('', 'full', None,
36 ('', 'full', None,
37 _('apply filemap changes by converting all files again')),
37 _('apply filemap changes by converting all files again')),
38 ('', 'splicemap', '', _('splice synthesized history into place'),
38 ('', 'splicemap', '', _('splice synthesized history into place'),
39 _('FILE')),
39 _('FILE')),
40 ('', 'branchmap', '', _('change branch names while converting'),
40 ('', 'branchmap', '', _('change branch names while converting'),
41 _('FILE')),
41 _('FILE')),
42 ('', 'branchsort', None, _('try to sort changesets by branches')),
42 ('', 'branchsort', None, _('try to sort changesets by branches')),
43 ('', 'datesort', None, _('try to sort changesets by date')),
43 ('', 'datesort', None, _('try to sort changesets by date')),
44 ('', 'sourcesort', None, _('preserve source changesets order')),
44 ('', 'sourcesort', None, _('preserve source changesets order')),
45 ('', 'closesort', None, _('try to reorder closed revisions'))],
45 ('', 'closesort', None, _('try to reorder closed revisions'))],
46 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]'),
46 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]'),
47 norepo=True)
47 norepo=True)
48 def convert(ui, src, dest=None, revmapfile=None, **opts):
48 def convert(ui, src, dest=None, revmapfile=None, **opts):
49 """convert a foreign SCM repository to a Mercurial one.
49 """convert a foreign SCM repository to a Mercurial one.
50
50
51 Accepted source formats [identifiers]:
51 Accepted source formats [identifiers]:
52
52
53 - Mercurial [hg]
53 - Mercurial [hg]
54 - CVS [cvs]
54 - CVS [cvs]
55 - Darcs [darcs]
55 - Darcs [darcs]
56 - git [git]
56 - git [git]
57 - Subversion [svn]
57 - Subversion [svn]
58 - Monotone [mtn]
58 - Monotone [mtn]
59 - GNU Arch [gnuarch]
59 - GNU Arch [gnuarch]
60 - Bazaar [bzr]
60 - Bazaar [bzr]
61 - Perforce [p4]
61 - Perforce [p4]
62
62
63 Accepted destination formats [identifiers]:
63 Accepted destination formats [identifiers]:
64
64
65 - Mercurial [hg]
65 - Mercurial [hg]
66 - Subversion [svn] (history on branches is not preserved)
66 - Subversion [svn] (history on branches is not preserved)
67
67
68 If no revision is given, all revisions will be converted.
68 If no revision is given, all revisions will be converted.
69 Otherwise, convert will only import up to the named revision
69 Otherwise, convert will only import up to the named revision
70 (given in a format understood by the source).
70 (given in a format understood by the source).
71
71
72 If no destination directory name is specified, it defaults to the
72 If no destination directory name is specified, it defaults to the
73 basename of the source with ``-hg`` appended. If the destination
73 basename of the source with ``-hg`` appended. If the destination
74 repository doesn't exist, it will be created.
74 repository doesn't exist, it will be created.
75
75
76 By default, all sources except Mercurial will use --branchsort.
76 By default, all sources except Mercurial will use --branchsort.
77 Mercurial uses --sourcesort to preserve original revision numbers
77 Mercurial uses --sourcesort to preserve original revision numbers
78 order. Sort modes have the following effects:
78 order. Sort modes have the following effects:
79
79
80 --branchsort convert from parent to child revision when possible,
80 --branchsort convert from parent to child revision when possible,
81 which means branches are usually converted one after
81 which means branches are usually converted one after
82 the other. It generates more compact repositories.
82 the other. It generates more compact repositories.
83
83
84 --datesort sort revisions by date. Converted repositories have
84 --datesort sort revisions by date. Converted repositories have
85 good-looking changelogs but are often an order of
85 good-looking changelogs but are often an order of
86 magnitude larger than the same ones generated by
86 magnitude larger than the same ones generated by
87 --branchsort.
87 --branchsort.
88
88
89 --sourcesort try to preserve source revisions order, only
89 --sourcesort try to preserve source revisions order, only
90 supported by Mercurial sources.
90 supported by Mercurial sources.
91
91
92 --closesort try to move closed revisions as close as possible
92 --closesort try to move closed revisions as close as possible
93 to parent branches, only supported by Mercurial
93 to parent branches, only supported by Mercurial
94 sources.
94 sources.
95
95
96 If ``REVMAP`` isn't given, it will be put in a default location
96 If ``REVMAP`` isn't given, it will be put in a default location
97 (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
97 (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple
98 text file that maps each source commit ID to the destination ID
98 text file that maps each source commit ID to the destination ID
99 for that revision, like so::
99 for that revision, like so::
100
100
101 <source ID> <destination ID>
101 <source ID> <destination ID>
102
102
103 If the file doesn't exist, it's automatically created. It's
103 If the file doesn't exist, it's automatically created. It's
104 updated on each commit copied, so :hg:`convert` can be interrupted
104 updated on each commit copied, so :hg:`convert` can be interrupted
105 and can be run repeatedly to copy new commits.
105 and can be run repeatedly to copy new commits.
106
106
107 The authormap is a simple text file that maps each source commit
107 The authormap is a simple text file that maps each source commit
108 author to a destination commit author. It is handy for source SCMs
108 author to a destination commit author. It is handy for source SCMs
109 that use unix logins to identify authors (e.g.: CVS). One line per
109 that use unix logins to identify authors (e.g.: CVS). One line per
110 author mapping and the line format is::
110 author mapping and the line format is::
111
111
112 source author = destination author
112 source author = destination author
113
113
114 Empty lines and lines starting with a ``#`` are ignored.
114 Empty lines and lines starting with a ``#`` are ignored.
115
115
116 The filemap is a file that allows filtering and remapping of files
116 The filemap is a file that allows filtering and remapping of files
117 and directories. Each line can contain one of the following
117 and directories. Each line can contain one of the following
118 directives::
118 directives::
119
119
120 include path/to/file-or-dir
120 include path/to/file-or-dir
121
121
122 exclude path/to/file-or-dir
122 exclude path/to/file-or-dir
123
123
124 rename path/to/source path/to/destination
124 rename path/to/source path/to/destination
125
125
126 Comment lines start with ``#``. A specified path matches if it
126 Comment lines start with ``#``. A specified path matches if it
127 equals the full relative name of a file or one of its parent
127 equals the full relative name of a file or one of its parent
128 directories. The ``include`` or ``exclude`` directive with the
128 directories. The ``include`` or ``exclude`` directive with the
129 longest matching path applies, so line order does not matter.
129 longest matching path applies, so line order does not matter.
130
130
131 The ``include`` directive causes a file, or all files under a
131 The ``include`` directive causes a file, or all files under a
132 directory, to be included in the destination repository. The default
132 directory, to be included in the destination repository. The default
133 if there are no ``include`` statements is to include everything.
133 if there are no ``include`` statements is to include everything.
134 If there are any ``include`` statements, nothing else is included.
134 If there are any ``include`` statements, nothing else is included.
135 The ``exclude`` directive causes files or directories to
135 The ``exclude`` directive causes files or directories to
136 be omitted. The ``rename`` directive renames a file or directory if
136 be omitted. The ``rename`` directive renames a file or directory if
137 it is converted. To rename from a subdirectory into the root of
137 it is converted. To rename from a subdirectory into the root of
138 the repository, use ``.`` as the path to rename to.
138 the repository, use ``.`` as the path to rename to.
139
139
140 ``--full`` will make sure the converted changesets contain exactly
140 ``--full`` will make sure the converted changesets contain exactly
141 the right files with the right content. It will make a full
141 the right files with the right content. It will make a full
142 conversion of all files, not just the ones that have
142 conversion of all files, not just the ones that have
143 changed. Files that already are correct will not be changed. This
143 changed. Files that already are correct will not be changed. This
144 can be used to apply filemap changes when converting
144 can be used to apply filemap changes when converting
145 incrementally. This is currently only supported for Mercurial and
145 incrementally. This is currently only supported for Mercurial and
146 Subversion.
146 Subversion.
147
147
148 The splicemap is a file that allows insertion of synthetic
148 The splicemap is a file that allows insertion of synthetic
149 history, letting you specify the parents of a revision. This is
149 history, letting you specify the parents of a revision. This is
150 useful if you want to e.g. give a Subversion merge two parents, or
150 useful if you want to e.g. give a Subversion merge two parents, or
151 graft two disconnected series of history together. Each entry
151 graft two disconnected series of history together. Each entry
152 contains a key, followed by a space, followed by one or two
152 contains a key, followed by a space, followed by one or two
153 comma-separated values::
153 comma-separated values::
154
154
155 key parent1, parent2
155 key parent1, parent2
156
156
157 The key is the revision ID in the source
157 The key is the revision ID in the source
158 revision control system whose parents should be modified (same
158 revision control system whose parents should be modified (same
159 format as a key in .hg/shamap). The values are the revision IDs
159 format as a key in .hg/shamap). The values are the revision IDs
160 (in either the source or destination revision control system) that
160 (in either the source or destination revision control system) that
161 should be used as the new parents for that node. For example, if
161 should be used as the new parents for that node. For example, if
162 you have merged "release-1.0" into "trunk", then you should
162 you have merged "release-1.0" into "trunk", then you should
163 specify the revision on "trunk" as the first parent and the one on
163 specify the revision on "trunk" as the first parent and the one on
164 the "release-1.0" branch as the second.
164 the "release-1.0" branch as the second.
165
165
166 The branchmap is a file that allows you to rename a branch when it is
166 The branchmap is a file that allows you to rename a branch when it is
167 being brought in from whatever external repository. When used in
167 being brought in from whatever external repository. When used in
168 conjunction with a splicemap, it allows for a powerful combination
168 conjunction with a splicemap, it allows for a powerful combination
169 to help fix even the most badly mismanaged repositories and turn them
169 to help fix even the most badly mismanaged repositories and turn them
170 into nicely structured Mercurial repositories. The branchmap contains
170 into nicely structured Mercurial repositories. The branchmap contains
171 lines of the form::
171 lines of the form::
172
172
173 original_branch_name new_branch_name
173 original_branch_name new_branch_name
174
174
175 where "original_branch_name" is the name of the branch in the
175 where "original_branch_name" is the name of the branch in the
176 source repository, and "new_branch_name" is the name of the branch
176 source repository, and "new_branch_name" is the name of the branch
177 is the destination repository. No whitespace is allowed in the
177 is the destination repository. No whitespace is allowed in the
178 branch names. This can be used to (for instance) move code in one
178 branch names. This can be used to (for instance) move code in one
179 repository from "default" to a named branch.
179 repository from "default" to a named branch.
180
180
181 Mercurial Source
181 Mercurial Source
182 ################
182 ################
183
183
184 The Mercurial source recognizes the following configuration
184 The Mercurial source recognizes the following configuration
185 options, which you can set on the command line with ``--config``:
185 options, which you can set on the command line with ``--config``:
186
186
187 :convert.hg.ignoreerrors: ignore integrity errors when reading.
187 :convert.hg.ignoreerrors: ignore integrity errors when reading.
188 Use it to fix Mercurial repositories with missing revlogs, by
188 Use it to fix Mercurial repositories with missing revlogs, by
189 converting from and to Mercurial. Default is False.
189 converting from and to Mercurial. Default is False.
190
190
191 :convert.hg.saverev: store original revision ID in changeset
191 :convert.hg.saverev: store original revision ID in changeset
192 (forces target IDs to change). It takes a boolean argument and
192 (forces target IDs to change). It takes a boolean argument and
193 defaults to False.
193 defaults to False.
194
194
195 :convert.hg.revs: revset specifying the source revisions to convert.
195 :convert.hg.revs: revset specifying the source revisions to convert.
196
196
197 CVS Source
197 CVS Source
198 ##########
198 ##########
199
199
200 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
200 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
201 to indicate the starting point of what will be converted. Direct
201 to indicate the starting point of what will be converted. Direct
202 access to the repository files is not needed, unless of course the
202 access to the repository files is not needed, unless of course the
203 repository is ``:local:``. The conversion uses the top level
203 repository is ``:local:``. The conversion uses the top level
204 directory in the sandbox to find the CVS repository, and then uses
204 directory in the sandbox to find the CVS repository, and then uses
205 CVS rlog commands to find files to convert. This means that unless
205 CVS rlog commands to find files to convert. This means that unless
206 a filemap is given, all files under the starting directory will be
206 a filemap is given, all files under the starting directory will be
207 converted, and that any directory reorganization in the CVS
207 converted, and that any directory reorganization in the CVS
208 sandbox is ignored.
208 sandbox is ignored.
209
209
210 The following options can be used with ``--config``:
210 The following options can be used with ``--config``:
211
211
212 :convert.cvsps.cache: Set to False to disable remote log caching,
212 :convert.cvsps.cache: Set to False to disable remote log caching,
213 for testing and debugging purposes. Default is True.
213 for testing and debugging purposes. Default is True.
214
214
215 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
215 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
216 allowed between commits with identical user and log message in
216 allowed between commits with identical user and log message in
217 a single changeset. When very large files were checked in as
217 a single changeset. When very large files were checked in as
218 part of a changeset then the default may not be long enough.
218 part of a changeset then the default may not be long enough.
219 The default is 60.
219 The default is 60.
220
220
221 :convert.cvsps.mergeto: Specify a regular expression to which
221 :convert.cvsps.mergeto: Specify a regular expression to which
222 commit log messages are matched. If a match occurs, then the
222 commit log messages are matched. If a match occurs, then the
223 conversion process will insert a dummy revision merging the
223 conversion process will insert a dummy revision merging the
224 branch on which this log message occurs to the branch
224 branch on which this log message occurs to the branch
225 indicated in the regex. Default is ``{{mergetobranch
225 indicated in the regex. Default is ``{{mergetobranch
226 ([-\\w]+)}}``
226 ([-\\w]+)}}``
227
227
228 :convert.cvsps.mergefrom: Specify a regular expression to which
228 :convert.cvsps.mergefrom: Specify a regular expression to which
229 commit log messages are matched. If a match occurs, then the
229 commit log messages are matched. If a match occurs, then the
230 conversion process will add the most recent revision on the
230 conversion process will add the most recent revision on the
231 branch indicated in the regex as the second parent of the
231 branch indicated in the regex as the second parent of the
232 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
232 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
233
233
234 :convert.localtimezone: use local time (as determined by the TZ
234 :convert.localtimezone: use local time (as determined by the TZ
235 environment variable) for changeset date/times. The default
235 environment variable) for changeset date/times. The default
236 is False (use UTC).
236 is False (use UTC).
237
237
238 :hooks.cvslog: Specify a Python function to be called at the end of
238 :hooks.cvslog: Specify a Python function to be called at the end of
239 gathering the CVS log. The function is passed a list with the
239 gathering the CVS log. The function is passed a list with the
240 log entries, and can modify the entries in-place, or add or
240 log entries, and can modify the entries in-place, or add or
241 delete them.
241 delete them.
242
242
243 :hooks.cvschangesets: Specify a Python function to be called after
243 :hooks.cvschangesets: Specify a Python function to be called after
244 the changesets are calculated from the CVS log. The
244 the changesets are calculated from the CVS log. The
245 function is passed a list with the changeset entries, and can
245 function is passed a list with the changeset entries, and can
246 modify the changesets in-place, or add or delete them.
246 modify the changesets in-place, or add or delete them.
247
247
248 An additional "debugcvsps" Mercurial command allows the builtin
248 An additional "debugcvsps" Mercurial command allows the builtin
249 changeset merging code to be run without doing a conversion. Its
249 changeset merging code to be run without doing a conversion. Its
250 parameters and output are similar to that of cvsps 2.1. Please see
250 parameters and output are similar to that of cvsps 2.1. Please see
251 the command help for more details.
251 the command help for more details.
252
252
253 Subversion Source
253 Subversion Source
254 #################
254 #################
255
255
256 Subversion source detects classical trunk/branches/tags layouts.
256 Subversion source detects classical trunk/branches/tags layouts.
257 By default, the supplied ``svn://repo/path/`` source URL is
257 By default, the supplied ``svn://repo/path/`` source URL is
258 converted as a single branch. If ``svn://repo/path/trunk`` exists
258 converted as a single branch. If ``svn://repo/path/trunk`` exists
259 it replaces the default branch. If ``svn://repo/path/branches``
259 it replaces the default branch. If ``svn://repo/path/branches``
260 exists, its subdirectories are listed as possible branches. If
260 exists, its subdirectories are listed as possible branches. If
261 ``svn://repo/path/tags`` exists, it is looked for tags referencing
261 ``svn://repo/path/tags`` exists, it is looked for tags referencing
262 converted branches. Default ``trunk``, ``branches`` and ``tags``
262 converted branches. Default ``trunk``, ``branches`` and ``tags``
263 values can be overridden with following options. Set them to paths
263 values can be overridden with following options. Set them to paths
264 relative to the source URL, or leave them blank to disable auto
264 relative to the source URL, or leave them blank to disable auto
265 detection.
265 detection.
266
266
267 The following options can be set with ``--config``:
267 The following options can be set with ``--config``:
268
268
269 :convert.svn.branches: specify the directory containing branches.
269 :convert.svn.branches: specify the directory containing branches.
270 The default is ``branches``.
270 The default is ``branches``.
271
271
272 :convert.svn.tags: specify the directory containing tags. The
272 :convert.svn.tags: specify the directory containing tags. The
273 default is ``tags``.
273 default is ``tags``.
274
274
275 :convert.svn.trunk: specify the name of the trunk branch. The
275 :convert.svn.trunk: specify the name of the trunk branch. The
276 default is ``trunk``.
276 default is ``trunk``.
277
277
278 :convert.localtimezone: use local time (as determined by the TZ
278 :convert.localtimezone: use local time (as determined by the TZ
279 environment variable) for changeset date/times. The default
279 environment variable) for changeset date/times. The default
280 is False (use UTC).
280 is False (use UTC).
281
281
282 Source history can be retrieved starting at a specific revision,
282 Source history can be retrieved starting at a specific revision,
283 instead of being integrally converted. Only single branch
283 instead of being integrally converted. Only single branch
284 conversions are supported.
284 conversions are supported.
285
285
286 :convert.svn.startrev: specify start Subversion revision number.
286 :convert.svn.startrev: specify start Subversion revision number.
287 The default is 0.
287 The default is 0.
288
288
289 Git Source
289 Git Source
290 ##########
290 ##########
291
291
292 The Git importer converts commits from all reachable branches (refs
292 The Git importer converts commits from all reachable branches (refs
293 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
293 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
294 Branches are converted to bookmarks with the same name, with the
294 Branches are converted to bookmarks with the same name, with the
295 leading 'refs/heads' stripped. Git submodules are converted to Git
295 leading 'refs/heads' stripped. Git submodules are converted to Git
296 subrepos in Mercurial.
296 subrepos in Mercurial.
297
297
298 The following options can be set with ``--config``:
298 The following options can be set with ``--config``:
299
299
300 :convert.git.similarity: specify how similar files modified in a
300 :convert.git.similarity: specify how similar files modified in a
301 commit must be to be imported as renames or copies, as a
301 commit must be to be imported as renames or copies, as a
302 percentage between ``0`` (disabled) and ``100`` (files must be
302 percentage between ``0`` (disabled) and ``100`` (files must be
303 identical). For example, ``90`` means that a delete/add pair will
303 identical). For example, ``90`` means that a delete/add pair will
304 be imported as a rename if more than 90% of the file hasn't
304 be imported as a rename if more than 90% of the file hasn't
305 changed. The default is ``50``.
305 changed. The default is ``50``.
306
306
307 :convert.git.findcopiesharder: while detecting copies, look at all
307 :convert.git.findcopiesharder: while detecting copies, look at all
308 files in the working copy instead of just changed ones. This
308 files in the working copy instead of just changed ones. This
309 is very expensive for large projects, and is only effective when
309 is very expensive for large projects, and is only effective when
310 ``convert.git.similarity`` is greater than 0. The default is False.
310 ``convert.git.similarity`` is greater than 0. The default is False.
311
311
312 Perforce Source
312 Perforce Source
313 ###############
313 ###############
314
314
315 The Perforce (P4) importer can be given a p4 depot path or a
315 The Perforce (P4) importer can be given a p4 depot path or a
316 client specification as source. It will convert all files in the
316 client specification as source. It will convert all files in the
317 source to a flat Mercurial repository, ignoring labels, branches
317 source to a flat Mercurial repository, ignoring labels, branches
318 and integrations. Note that when a depot path is given you then
318 and integrations. Note that when a depot path is given you then
319 usually should specify a target directory, because otherwise the
319 usually should specify a target directory, because otherwise the
320 target may be named ``...-hg``.
320 target may be named ``...-hg``.
321
321
322 It is possible to limit the amount of source history to be
322 It is possible to limit the amount of source history to be
323 converted by specifying an initial Perforce revision:
323 converted by specifying an initial Perforce revision:
324
324
325 :convert.p4.startrev: specify initial Perforce revision (a
325 :convert.p4.startrev: specify initial Perforce revision (a
326 Perforce changelist number).
326 Perforce changelist number).
327
327
328 Mercurial Destination
328 Mercurial Destination
329 #####################
329 #####################
330
330
331 The Mercurial destination will recognize Mercurial subrepositories in the
331 The Mercurial destination will recognize Mercurial subrepositories in the
332 destination directory, and update the .hgsubstate file automatically if the
332 destination directory, and update the .hgsubstate file automatically if the
333 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
333 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
334 Converting a repository with subrepositories requires converting a single
334 Converting a repository with subrepositories requires converting a single
335 repository at a time, from the bottom up.
335 repository at a time, from the bottom up.
336
336
337 .. container:: verbose
337 .. container:: verbose
338
338
339 An example showing how to convert a repository with subrepositories::
339 An example showing how to convert a repository with subrepositories::
340
340
341 # so convert knows the type when it sees a non empty destination
341 # so convert knows the type when it sees a non empty destination
342 $ hg init converted
342 $ hg init converted
343
343
344 $ hg convert orig/sub1 converted/sub1
344 $ hg convert orig/sub1 converted/sub1
345 $ hg convert orig/sub2 converted/sub2
345 $ hg convert orig/sub2 converted/sub2
346 $ hg convert orig converted
346 $ hg convert orig converted
347
347
348 The following options are supported:
348 The following options are supported:
349
349
350 :convert.hg.clonebranches: dispatch source branches in separate
350 :convert.hg.clonebranches: dispatch source branches in separate
351 clones. The default is False.
351 clones. The default is False.
352
352
353 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
353 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
354 ``default``.
354 ``default``.
355
355
356 :convert.hg.usebranchnames: preserve branch names. The default is
356 :convert.hg.usebranchnames: preserve branch names. The default is
357 True.
357 True.
358
358
359 :convert.hg.sourcename: records the given string as a 'convert_source' extra
360 value on each commit made in the target repository. The default is None.
361
359 All Destinations
362 All Destinations
360 ################
363 ################
361
364
362 All destination types accept the following options:
365 All destination types accept the following options:
363
366
364 :convert.skiptags: does not convert tags from the source repo to the target
367 :convert.skiptags: does not convert tags from the source repo to the target
365 repo. The default is False.
368 repo. The default is False.
366 """
369 """
367 return convcmd.convert(ui, src, dest, revmapfile, **opts)
370 return convcmd.convert(ui, src, dest, revmapfile, **opts)
368
371
369 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
372 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
370 def debugsvnlog(ui, **opts):
373 def debugsvnlog(ui, **opts):
371 return subversion.debugsvnlog(ui, **opts)
374 return subversion.debugsvnlog(ui, **opts)
372
375
373 @command('debugcvsps',
376 @command('debugcvsps',
374 [
377 [
375 # Main options shared with cvsps-2.1
378 # Main options shared with cvsps-2.1
376 ('b', 'branches', [], _('only return changes on specified branches')),
379 ('b', 'branches', [], _('only return changes on specified branches')),
377 ('p', 'prefix', '', _('prefix to remove from file names')),
380 ('p', 'prefix', '', _('prefix to remove from file names')),
378 ('r', 'revisions', [],
381 ('r', 'revisions', [],
379 _('only return changes after or between specified tags')),
382 _('only return changes after or between specified tags')),
380 ('u', 'update-cache', None, _("update cvs log cache")),
383 ('u', 'update-cache', None, _("update cvs log cache")),
381 ('x', 'new-cache', None, _("create new cvs log cache")),
384 ('x', 'new-cache', None, _("create new cvs log cache")),
382 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
385 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
383 ('', 'root', '', _('specify cvsroot')),
386 ('', 'root', '', _('specify cvsroot')),
384 # Options specific to builtin cvsps
387 # Options specific to builtin cvsps
385 ('', 'parents', '', _('show parent changesets')),
388 ('', 'parents', '', _('show parent changesets')),
386 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
389 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
387 # Options that are ignored for compatibility with cvsps-2.1
390 # Options that are ignored for compatibility with cvsps-2.1
388 ('A', 'cvs-direct', None, _('ignored for compatibility')),
391 ('A', 'cvs-direct', None, _('ignored for compatibility')),
389 ],
392 ],
390 _('hg debugcvsps [OPTION]... [PATH]...'),
393 _('hg debugcvsps [OPTION]... [PATH]...'),
391 norepo=True)
394 norepo=True)
392 def debugcvsps(ui, *args, **opts):
395 def debugcvsps(ui, *args, **opts):
393 '''create changeset information from CVS
396 '''create changeset information from CVS
394
397
395 This command is intended as a debugging tool for the CVS to
398 This command is intended as a debugging tool for the CVS to
396 Mercurial converter, and can be used as a direct replacement for
399 Mercurial converter, and can be used as a direct replacement for
397 cvsps.
400 cvsps.
398
401
399 Hg debugcvsps reads the CVS rlog for current directory (or any
402 Hg debugcvsps reads the CVS rlog for current directory (or any
400 named directory) in the CVS repository, and converts the log to a
403 named directory) in the CVS repository, and converts the log to a
401 series of changesets based on matching commit log entries and
404 series of changesets based on matching commit log entries and
402 dates.'''
405 dates.'''
403 return cvsps.debugcvsps(ui, *args, **opts)
406 return cvsps.debugcvsps(ui, *args, **opts)
404
407
405 def kwconverted(ctx, name):
408 def kwconverted(ctx, name):
406 rev = ctx.extra().get('convert_revision', '')
409 rev = ctx.extra().get('convert_revision', '')
407 if rev.startswith('svn:'):
410 if rev.startswith('svn:'):
408 if name == 'svnrev':
411 if name == 'svnrev':
409 return str(subversion.revsplit(rev)[2])
412 return str(subversion.revsplit(rev)[2])
410 elif name == 'svnpath':
413 elif name == 'svnpath':
411 return subversion.revsplit(rev)[1]
414 return subversion.revsplit(rev)[1]
412 elif name == 'svnuuid':
415 elif name == 'svnuuid':
413 return subversion.revsplit(rev)[0]
416 return subversion.revsplit(rev)[0]
414 return rev
417 return rev
415
418
416 def kwsvnrev(repo, ctx, **args):
419 def kwsvnrev(repo, ctx, **args):
417 """:svnrev: String. Converted subversion revision number."""
420 """:svnrev: String. Converted subversion revision number."""
418 return kwconverted(ctx, 'svnrev')
421 return kwconverted(ctx, 'svnrev')
419
422
420 def kwsvnpath(repo, ctx, **args):
423 def kwsvnpath(repo, ctx, **args):
421 """:svnpath: String. Converted subversion revision project path."""
424 """:svnpath: String. Converted subversion revision project path."""
422 return kwconverted(ctx, 'svnpath')
425 return kwconverted(ctx, 'svnpath')
423
426
424 def kwsvnuuid(repo, ctx, **args):
427 def kwsvnuuid(repo, ctx, **args):
425 """:svnuuid: String. Converted subversion revision repository identifier."""
428 """:svnuuid: String. Converted subversion revision repository identifier."""
426 return kwconverted(ctx, 'svnuuid')
429 return kwconverted(ctx, 'svnuuid')
427
430
428 def extsetup(ui):
431 def extsetup(ui):
429 templatekw.keywords['svnrev'] = kwsvnrev
432 templatekw.keywords['svnrev'] = kwsvnrev
430 templatekw.keywords['svnpath'] = kwsvnpath
433 templatekw.keywords['svnpath'] = kwsvnpath
431 templatekw.keywords['svnuuid'] = kwsvnuuid
434 templatekw.keywords['svnuuid'] = kwsvnuuid
432
435
433 # tell hggettext to extract docstrings from these functions:
436 # tell hggettext to extract docstrings from these functions:
434 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
437 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
@@ -1,564 +1,568 b''
1 # hg.py - hg backend for convert extension
1 # hg.py - hg backend for convert extension
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # Notes for hg->hg conversion:
8 # Notes for hg->hg conversion:
9 #
9 #
10 # * Old versions of Mercurial didn't trim the whitespace from the ends
10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 # of commit messages, but new versions do. Changesets created by
11 # of commit messages, but new versions do. Changesets created by
12 # those older versions, then converted, may thus have different
12 # those older versions, then converted, may thus have different
13 # hashes for changesets that are otherwise identical.
13 # hashes for changesets that are otherwise identical.
14 #
14 #
15 # * Using "--config convert.hg.saverev=true" will make the source
15 # * Using "--config convert.hg.saverev=true" will make the source
16 # identifier to be stored in the converted revision. This will cause
16 # identifier to be stored in the converted revision. This will cause
17 # the converted revision to have a different identity than the
17 # the converted revision to have a different identity than the
18 # source.
18 # source.
19
19
20
20
21 import os, time, cStringIO
21 import os, time, cStringIO
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 from mercurial.node import bin, hex, nullid
23 from mercurial.node import bin, hex, nullid
24 from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
24 from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
25 from mercurial import phases
25 from mercurial import phases
26
26
27 from common import NoRepo, commit, converter_source, converter_sink, mapfile
27 from common import NoRepo, commit, converter_source, converter_sink, mapfile
28
28
29 import re
29 import re
30 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
30 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
31
31
32 class mercurial_sink(converter_sink):
32 class mercurial_sink(converter_sink):
33 def __init__(self, ui, path):
33 def __init__(self, ui, path):
34 converter_sink.__init__(self, ui, path)
34 converter_sink.__init__(self, ui, path)
35 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
35 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
36 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
36 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
37 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
37 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
38 self.lastbranch = None
38 self.lastbranch = None
39 if os.path.isdir(path) and len(os.listdir(path)) > 0:
39 if os.path.isdir(path) and len(os.listdir(path)) > 0:
40 try:
40 try:
41 self.repo = hg.repository(self.ui, path)
41 self.repo = hg.repository(self.ui, path)
42 if not self.repo.local():
42 if not self.repo.local():
43 raise NoRepo(_('%s is not a local Mercurial repository')
43 raise NoRepo(_('%s is not a local Mercurial repository')
44 % path)
44 % path)
45 except error.RepoError as err:
45 except error.RepoError as err:
46 ui.traceback()
46 ui.traceback()
47 raise NoRepo(err.args[0])
47 raise NoRepo(err.args[0])
48 else:
48 else:
49 try:
49 try:
50 ui.status(_('initializing destination %s repository\n') % path)
50 ui.status(_('initializing destination %s repository\n') % path)
51 self.repo = hg.repository(self.ui, path, create=True)
51 self.repo = hg.repository(self.ui, path, create=True)
52 if not self.repo.local():
52 if not self.repo.local():
53 raise NoRepo(_('%s is not a local Mercurial repository')
53 raise NoRepo(_('%s is not a local Mercurial repository')
54 % path)
54 % path)
55 self.created.append(path)
55 self.created.append(path)
56 except error.RepoError:
56 except error.RepoError:
57 ui.traceback()
57 ui.traceback()
58 raise NoRepo(_("could not create hg repository %s as sink")
58 raise NoRepo(_("could not create hg repository %s as sink")
59 % path)
59 % path)
60 self.lock = None
60 self.lock = None
61 self.wlock = None
61 self.wlock = None
62 self.filemapmode = False
62 self.filemapmode = False
63 self.subrevmaps = {}
63 self.subrevmaps = {}
64
64
65 def before(self):
65 def before(self):
66 self.ui.debug('run hg sink pre-conversion action\n')
66 self.ui.debug('run hg sink pre-conversion action\n')
67 self.wlock = self.repo.wlock()
67 self.wlock = self.repo.wlock()
68 self.lock = self.repo.lock()
68 self.lock = self.repo.lock()
69
69
70 def after(self):
70 def after(self):
71 self.ui.debug('run hg sink post-conversion action\n')
71 self.ui.debug('run hg sink post-conversion action\n')
72 if self.lock:
72 if self.lock:
73 self.lock.release()
73 self.lock.release()
74 if self.wlock:
74 if self.wlock:
75 self.wlock.release()
75 self.wlock.release()
76
76
77 def revmapfile(self):
77 def revmapfile(self):
78 return self.repo.join("shamap")
78 return self.repo.join("shamap")
79
79
80 def authorfile(self):
80 def authorfile(self):
81 return self.repo.join("authormap")
81 return self.repo.join("authormap")
82
82
83 def setbranch(self, branch, pbranches):
83 def setbranch(self, branch, pbranches):
84 if not self.clonebranches:
84 if not self.clonebranches:
85 return
85 return
86
86
87 setbranch = (branch != self.lastbranch)
87 setbranch = (branch != self.lastbranch)
88 self.lastbranch = branch
88 self.lastbranch = branch
89 if not branch:
89 if not branch:
90 branch = 'default'
90 branch = 'default'
91 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
91 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
92 if pbranches:
92 if pbranches:
93 pbranch = pbranches[0][1]
93 pbranch = pbranches[0][1]
94 else:
94 else:
95 pbranch = 'default'
95 pbranch = 'default'
96
96
97 branchpath = os.path.join(self.path, branch)
97 branchpath = os.path.join(self.path, branch)
98 if setbranch:
98 if setbranch:
99 self.after()
99 self.after()
100 try:
100 try:
101 self.repo = hg.repository(self.ui, branchpath)
101 self.repo = hg.repository(self.ui, branchpath)
102 except Exception:
102 except Exception:
103 self.repo = hg.repository(self.ui, branchpath, create=True)
103 self.repo = hg.repository(self.ui, branchpath, create=True)
104 self.before()
104 self.before()
105
105
106 # pbranches may bring revisions from other branches (merge parents)
106 # pbranches may bring revisions from other branches (merge parents)
107 # Make sure we have them, or pull them.
107 # Make sure we have them, or pull them.
108 missings = {}
108 missings = {}
109 for b in pbranches:
109 for b in pbranches:
110 try:
110 try:
111 self.repo.lookup(b[0])
111 self.repo.lookup(b[0])
112 except Exception:
112 except Exception:
113 missings.setdefault(b[1], []).append(b[0])
113 missings.setdefault(b[1], []).append(b[0])
114
114
115 if missings:
115 if missings:
116 self.after()
116 self.after()
117 for pbranch, heads in sorted(missings.iteritems()):
117 for pbranch, heads in sorted(missings.iteritems()):
118 pbranchpath = os.path.join(self.path, pbranch)
118 pbranchpath = os.path.join(self.path, pbranch)
119 prepo = hg.peer(self.ui, {}, pbranchpath)
119 prepo = hg.peer(self.ui, {}, pbranchpath)
120 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
120 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
121 exchange.pull(self.repo, prepo,
121 exchange.pull(self.repo, prepo,
122 [prepo.lookup(h) for h in heads])
122 [prepo.lookup(h) for h in heads])
123 self.before()
123 self.before()
124
124
125 def _rewritetags(self, source, revmap, data):
125 def _rewritetags(self, source, revmap, data):
126 fp = cStringIO.StringIO()
126 fp = cStringIO.StringIO()
127 for line in data.splitlines():
127 for line in data.splitlines():
128 s = line.split(' ', 1)
128 s = line.split(' ', 1)
129 if len(s) != 2:
129 if len(s) != 2:
130 continue
130 continue
131 revid = revmap.get(source.lookuprev(s[0]))
131 revid = revmap.get(source.lookuprev(s[0]))
132 if not revid:
132 if not revid:
133 if s[0] == hex(nullid):
133 if s[0] == hex(nullid):
134 revid = s[0]
134 revid = s[0]
135 else:
135 else:
136 continue
136 continue
137 fp.write('%s %s\n' % (revid, s[1]))
137 fp.write('%s %s\n' % (revid, s[1]))
138 return fp.getvalue()
138 return fp.getvalue()
139
139
140 def _rewritesubstate(self, source, data):
140 def _rewritesubstate(self, source, data):
141 fp = cStringIO.StringIO()
141 fp = cStringIO.StringIO()
142 for line in data.splitlines():
142 for line in data.splitlines():
143 s = line.split(' ', 1)
143 s = line.split(' ', 1)
144 if len(s) != 2:
144 if len(s) != 2:
145 continue
145 continue
146
146
147 revid = s[0]
147 revid = s[0]
148 subpath = s[1]
148 subpath = s[1]
149 if revid != hex(nullid):
149 if revid != hex(nullid):
150 revmap = self.subrevmaps.get(subpath)
150 revmap = self.subrevmaps.get(subpath)
151 if revmap is None:
151 if revmap is None:
152 revmap = mapfile(self.ui,
152 revmap = mapfile(self.ui,
153 self.repo.wjoin(subpath, '.hg/shamap'))
153 self.repo.wjoin(subpath, '.hg/shamap'))
154 self.subrevmaps[subpath] = revmap
154 self.subrevmaps[subpath] = revmap
155
155
156 # It is reasonable that one or more of the subrepos don't
156 # It is reasonable that one or more of the subrepos don't
157 # need to be converted, in which case they can be cloned
157 # need to be converted, in which case they can be cloned
158 # into place instead of converted. Therefore, only warn
158 # into place instead of converted. Therefore, only warn
159 # once.
159 # once.
160 msg = _('no ".hgsubstate" updates will be made for "%s"\n')
160 msg = _('no ".hgsubstate" updates will be made for "%s"\n')
161 if len(revmap) == 0:
161 if len(revmap) == 0:
162 sub = self.repo.wvfs.reljoin(subpath, '.hg')
162 sub = self.repo.wvfs.reljoin(subpath, '.hg')
163
163
164 if self.repo.wvfs.exists(sub):
164 if self.repo.wvfs.exists(sub):
165 self.ui.warn(msg % subpath)
165 self.ui.warn(msg % subpath)
166
166
167 newid = revmap.get(revid)
167 newid = revmap.get(revid)
168 if not newid:
168 if not newid:
169 if len(revmap) > 0:
169 if len(revmap) > 0:
170 self.ui.warn(_("%s is missing from %s/.hg/shamap\n") %
170 self.ui.warn(_("%s is missing from %s/.hg/shamap\n") %
171 (revid, subpath))
171 (revid, subpath))
172 else:
172 else:
173 revid = newid
173 revid = newid
174
174
175 fp.write('%s %s\n' % (revid, subpath))
175 fp.write('%s %s\n' % (revid, subpath))
176
176
177 return fp.getvalue()
177 return fp.getvalue()
178
178
179 def putcommit(self, files, copies, parents, commit, source, revmap, full,
179 def putcommit(self, files, copies, parents, commit, source, revmap, full,
180 cleanp2):
180 cleanp2):
181 files = dict(files)
181 files = dict(files)
182
182
183 def getfilectx(repo, memctx, f):
183 def getfilectx(repo, memctx, f):
184 if p2ctx and f in cleanp2 and f not in copies:
184 if p2ctx and f in cleanp2 and f not in copies:
185 self.ui.debug('reusing %s from p2\n' % f)
185 self.ui.debug('reusing %s from p2\n' % f)
186 return p2ctx[f]
186 return p2ctx[f]
187 try:
187 try:
188 v = files[f]
188 v = files[f]
189 except KeyError:
189 except KeyError:
190 return None
190 return None
191 data, mode = source.getfile(f, v)
191 data, mode = source.getfile(f, v)
192 if data is None:
192 if data is None:
193 return None
193 return None
194 if f == '.hgtags':
194 if f == '.hgtags':
195 data = self._rewritetags(source, revmap, data)
195 data = self._rewritetags(source, revmap, data)
196 if f == '.hgsubstate':
196 if f == '.hgsubstate':
197 data = self._rewritesubstate(source, data)
197 data = self._rewritesubstate(source, data)
198 return context.memfilectx(self.repo, f, data, 'l' in mode,
198 return context.memfilectx(self.repo, f, data, 'l' in mode,
199 'x' in mode, copies.get(f))
199 'x' in mode, copies.get(f))
200
200
201 pl = []
201 pl = []
202 for p in parents:
202 for p in parents:
203 if p not in pl:
203 if p not in pl:
204 pl.append(p)
204 pl.append(p)
205 parents = pl
205 parents = pl
206 nparents = len(parents)
206 nparents = len(parents)
207 if self.filemapmode and nparents == 1:
207 if self.filemapmode and nparents == 1:
208 m1node = self.repo.changelog.read(bin(parents[0]))[0]
208 m1node = self.repo.changelog.read(bin(parents[0]))[0]
209 parent = parents[0]
209 parent = parents[0]
210
210
211 if len(parents) < 2:
211 if len(parents) < 2:
212 parents.append(nullid)
212 parents.append(nullid)
213 if len(parents) < 2:
213 if len(parents) < 2:
214 parents.append(nullid)
214 parents.append(nullid)
215 p2 = parents.pop(0)
215 p2 = parents.pop(0)
216
216
217 text = commit.desc
217 text = commit.desc
218
218
219 sha1s = re.findall(sha1re, text)
219 sha1s = re.findall(sha1re, text)
220 for sha1 in sha1s:
220 for sha1 in sha1s:
221 oldrev = source.lookuprev(sha1)
221 oldrev = source.lookuprev(sha1)
222 newrev = revmap.get(oldrev)
222 newrev = revmap.get(oldrev)
223 if newrev is not None:
223 if newrev is not None:
224 text = text.replace(sha1, newrev[:len(sha1)])
224 text = text.replace(sha1, newrev[:len(sha1)])
225
225
226 extra = commit.extra.copy()
226 extra = commit.extra.copy()
227
227
228 sourcename = self.repo.ui.config('convert', 'hg.sourcename')
229 if sourcename:
230 extra['convert_source'] = sourcename
231
228 for label in ('source', 'transplant_source', 'rebase_source',
232 for label in ('source', 'transplant_source', 'rebase_source',
229 'intermediate-source'):
233 'intermediate-source'):
230 node = extra.get(label)
234 node = extra.get(label)
231
235
232 if node is None:
236 if node is None:
233 continue
237 continue
234
238
235 # Only transplant stores its reference in binary
239 # Only transplant stores its reference in binary
236 if label == 'transplant_source':
240 if label == 'transplant_source':
237 node = hex(node)
241 node = hex(node)
238
242
239 newrev = revmap.get(node)
243 newrev = revmap.get(node)
240 if newrev is not None:
244 if newrev is not None:
241 if label == 'transplant_source':
245 if label == 'transplant_source':
242 newrev = bin(newrev)
246 newrev = bin(newrev)
243
247
244 extra[label] = newrev
248 extra[label] = newrev
245
249
246 if self.branchnames and commit.branch:
250 if self.branchnames and commit.branch:
247 extra['branch'] = commit.branch
251 extra['branch'] = commit.branch
248 if commit.rev and commit.saverev:
252 if commit.rev and commit.saverev:
249 extra['convert_revision'] = commit.rev
253 extra['convert_revision'] = commit.rev
250
254
251 while parents:
255 while parents:
252 p1 = p2
256 p1 = p2
253 p2 = parents.pop(0)
257 p2 = parents.pop(0)
254 p2ctx = None
258 p2ctx = None
255 if p2 != nullid:
259 if p2 != nullid:
256 p2ctx = self.repo[p2]
260 p2ctx = self.repo[p2]
257 fileset = set(files)
261 fileset = set(files)
258 if full:
262 if full:
259 fileset.update(self.repo[p1])
263 fileset.update(self.repo[p1])
260 fileset.update(self.repo[p2])
264 fileset.update(self.repo[p2])
261 ctx = context.memctx(self.repo, (p1, p2), text, fileset,
265 ctx = context.memctx(self.repo, (p1, p2), text, fileset,
262 getfilectx, commit.author, commit.date, extra)
266 getfilectx, commit.author, commit.date, extra)
263
267
264 # We won't know if the conversion changes the node until after the
268 # We won't know if the conversion changes the node until after the
265 # commit, so copy the source's phase for now.
269 # commit, so copy the source's phase for now.
266 self.repo.ui.setconfig('phases', 'new-commit',
270 self.repo.ui.setconfig('phases', 'new-commit',
267 phases.phasenames[commit.phase], 'convert')
271 phases.phasenames[commit.phase], 'convert')
268
272
269 tr = self.repo.transaction("convert")
273 tr = self.repo.transaction("convert")
270
274
271 try:
275 try:
272 node = hex(self.repo.commitctx(ctx))
276 node = hex(self.repo.commitctx(ctx))
273
277
274 # If the node value has changed, but the phase is lower than
278 # If the node value has changed, but the phase is lower than
275 # draft, set it back to draft since it hasn't been exposed
279 # draft, set it back to draft since it hasn't been exposed
276 # anywhere.
280 # anywhere.
277 if commit.rev != node:
281 if commit.rev != node:
278 ctx = self.repo[node]
282 ctx = self.repo[node]
279 if ctx.phase() < phases.draft:
283 if ctx.phase() < phases.draft:
280 phases.retractboundary(self.repo, tr, phases.draft,
284 phases.retractboundary(self.repo, tr, phases.draft,
281 [ctx.node()])
285 [ctx.node()])
282 tr.close()
286 tr.close()
283 finally:
287 finally:
284 tr.release()
288 tr.release()
285
289
286 text = "(octopus merge fixup)\n"
290 text = "(octopus merge fixup)\n"
287 p2 = node
291 p2 = node
288
292
289 if self.filemapmode and nparents == 1:
293 if self.filemapmode and nparents == 1:
290 man = self.repo.manifest
294 man = self.repo.manifest
291 mnode = self.repo.changelog.read(bin(p2))[0]
295 mnode = self.repo.changelog.read(bin(p2))[0]
292 closed = 'close' in commit.extra
296 closed = 'close' in commit.extra
293 if not closed and not man.cmp(m1node, man.revision(mnode)):
297 if not closed and not man.cmp(m1node, man.revision(mnode)):
294 self.ui.status(_("filtering out empty revision\n"))
298 self.ui.status(_("filtering out empty revision\n"))
295 self.repo.rollback(force=True)
299 self.repo.rollback(force=True)
296 return parent
300 return parent
297 return p2
301 return p2
298
302
299 def puttags(self, tags):
303 def puttags(self, tags):
300 try:
304 try:
301 parentctx = self.repo[self.tagsbranch]
305 parentctx = self.repo[self.tagsbranch]
302 tagparent = parentctx.node()
306 tagparent = parentctx.node()
303 except error.RepoError:
307 except error.RepoError:
304 parentctx = None
308 parentctx = None
305 tagparent = nullid
309 tagparent = nullid
306
310
307 oldlines = set()
311 oldlines = set()
308 for branch, heads in self.repo.branchmap().iteritems():
312 for branch, heads in self.repo.branchmap().iteritems():
309 for h in heads:
313 for h in heads:
310 if '.hgtags' in self.repo[h]:
314 if '.hgtags' in self.repo[h]:
311 oldlines.update(
315 oldlines.update(
312 set(self.repo[h]['.hgtags'].data().splitlines(True)))
316 set(self.repo[h]['.hgtags'].data().splitlines(True)))
313 oldlines = sorted(list(oldlines))
317 oldlines = sorted(list(oldlines))
314
318
315 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
319 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
316 if newlines == oldlines:
320 if newlines == oldlines:
317 return None, None
321 return None, None
318
322
319 # if the old and new tags match, then there is nothing to update
323 # if the old and new tags match, then there is nothing to update
320 oldtags = set()
324 oldtags = set()
321 newtags = set()
325 newtags = set()
322 for line in oldlines:
326 for line in oldlines:
323 s = line.strip().split(' ', 1)
327 s = line.strip().split(' ', 1)
324 if len(s) != 2:
328 if len(s) != 2:
325 continue
329 continue
326 oldtags.add(s[1])
330 oldtags.add(s[1])
327 for line in newlines:
331 for line in newlines:
328 s = line.strip().split(' ', 1)
332 s = line.strip().split(' ', 1)
329 if len(s) != 2:
333 if len(s) != 2:
330 continue
334 continue
331 if s[1] not in oldtags:
335 if s[1] not in oldtags:
332 newtags.add(s[1].strip())
336 newtags.add(s[1].strip())
333
337
334 if not newtags:
338 if not newtags:
335 return None, None
339 return None, None
336
340
337 data = "".join(newlines)
341 data = "".join(newlines)
338 def getfilectx(repo, memctx, f):
342 def getfilectx(repo, memctx, f):
339 return context.memfilectx(repo, f, data, False, False, None)
343 return context.memfilectx(repo, f, data, False, False, None)
340
344
341 self.ui.status(_("updating tags\n"))
345 self.ui.status(_("updating tags\n"))
342 date = "%s 0" % int(time.mktime(time.gmtime()))
346 date = "%s 0" % int(time.mktime(time.gmtime()))
343 extra = {'branch': self.tagsbranch}
347 extra = {'branch': self.tagsbranch}
344 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
348 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
345 [".hgtags"], getfilectx, "convert-repo", date,
349 [".hgtags"], getfilectx, "convert-repo", date,
346 extra)
350 extra)
347 node = self.repo.commitctx(ctx)
351 node = self.repo.commitctx(ctx)
348 return hex(node), hex(tagparent)
352 return hex(node), hex(tagparent)
349
353
350 def setfilemapmode(self, active):
354 def setfilemapmode(self, active):
351 self.filemapmode = active
355 self.filemapmode = active
352
356
353 def putbookmarks(self, updatedbookmark):
357 def putbookmarks(self, updatedbookmark):
354 if not len(updatedbookmark):
358 if not len(updatedbookmark):
355 return
359 return
356
360
357 self.ui.status(_("updating bookmarks\n"))
361 self.ui.status(_("updating bookmarks\n"))
358 destmarks = self.repo._bookmarks
362 destmarks = self.repo._bookmarks
359 for bookmark in updatedbookmark:
363 for bookmark in updatedbookmark:
360 destmarks[bookmark] = bin(updatedbookmark[bookmark])
364 destmarks[bookmark] = bin(updatedbookmark[bookmark])
361 destmarks.write()
365 destmarks.write()
362
366
363 def hascommitfrommap(self, rev):
367 def hascommitfrommap(self, rev):
364 # the exact semantics of clonebranches is unclear so we can't say no
368 # the exact semantics of clonebranches is unclear so we can't say no
365 return rev in self.repo or self.clonebranches
369 return rev in self.repo or self.clonebranches
366
370
367 def hascommitforsplicemap(self, rev):
371 def hascommitforsplicemap(self, rev):
368 if rev not in self.repo and self.clonebranches:
372 if rev not in self.repo and self.clonebranches:
369 raise util.Abort(_('revision %s not found in destination '
373 raise util.Abort(_('revision %s not found in destination '
370 'repository (lookups with clonebranches=true '
374 'repository (lookups with clonebranches=true '
371 'are not implemented)') % rev)
375 'are not implemented)') % rev)
372 return rev in self.repo
376 return rev in self.repo
373
377
374 class mercurial_source(converter_source):
378 class mercurial_source(converter_source):
375 def __init__(self, ui, path, revs=None):
379 def __init__(self, ui, path, revs=None):
376 converter_source.__init__(self, ui, path, revs)
380 converter_source.__init__(self, ui, path, revs)
377 if revs and len(revs) > 1:
381 if revs and len(revs) > 1:
378 raise util.Abort(_("mercurial source does not support specifying "
382 raise util.Abort(_("mercurial source does not support specifying "
379 "multiple revisions"))
383 "multiple revisions"))
380 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
384 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
381 self.ignored = set()
385 self.ignored = set()
382 self.saverev = ui.configbool('convert', 'hg.saverev', False)
386 self.saverev = ui.configbool('convert', 'hg.saverev', False)
383 try:
387 try:
384 self.repo = hg.repository(self.ui, path)
388 self.repo = hg.repository(self.ui, path)
385 # try to provoke an exception if this isn't really a hg
389 # try to provoke an exception if this isn't really a hg
386 # repo, but some other bogus compatible-looking url
390 # repo, but some other bogus compatible-looking url
387 if not self.repo.local():
391 if not self.repo.local():
388 raise error.RepoError
392 raise error.RepoError
389 except error.RepoError:
393 except error.RepoError:
390 ui.traceback()
394 ui.traceback()
391 raise NoRepo(_("%s is not a local Mercurial repository") % path)
395 raise NoRepo(_("%s is not a local Mercurial repository") % path)
392 self.lastrev = None
396 self.lastrev = None
393 self.lastctx = None
397 self.lastctx = None
394 self._changescache = None, None
398 self._changescache = None, None
395 self.convertfp = None
399 self.convertfp = None
396 # Restrict converted revisions to startrev descendants
400 # Restrict converted revisions to startrev descendants
397 startnode = ui.config('convert', 'hg.startrev')
401 startnode = ui.config('convert', 'hg.startrev')
398 hgrevs = ui.config('convert', 'hg.revs')
402 hgrevs = ui.config('convert', 'hg.revs')
399 if hgrevs is None:
403 if hgrevs is None:
400 if startnode is not None:
404 if startnode is not None:
401 try:
405 try:
402 startnode = self.repo.lookup(startnode)
406 startnode = self.repo.lookup(startnode)
403 except error.RepoError:
407 except error.RepoError:
404 raise util.Abort(_('%s is not a valid start revision')
408 raise util.Abort(_('%s is not a valid start revision')
405 % startnode)
409 % startnode)
406 startrev = self.repo.changelog.rev(startnode)
410 startrev = self.repo.changelog.rev(startnode)
407 children = {startnode: 1}
411 children = {startnode: 1}
408 for r in self.repo.changelog.descendants([startrev]):
412 for r in self.repo.changelog.descendants([startrev]):
409 children[self.repo.changelog.node(r)] = 1
413 children[self.repo.changelog.node(r)] = 1
410 self.keep = children.__contains__
414 self.keep = children.__contains__
411 else:
415 else:
412 self.keep = util.always
416 self.keep = util.always
413 if revs:
417 if revs:
414 self._heads = [self.repo[revs[0]].node()]
418 self._heads = [self.repo[revs[0]].node()]
415 else:
419 else:
416 self._heads = self.repo.heads()
420 self._heads = self.repo.heads()
417 else:
421 else:
418 if revs or startnode is not None:
422 if revs or startnode is not None:
419 raise util.Abort(_('hg.revs cannot be combined with '
423 raise util.Abort(_('hg.revs cannot be combined with '
420 'hg.startrev or --rev'))
424 'hg.startrev or --rev'))
421 nodes = set()
425 nodes = set()
422 parents = set()
426 parents = set()
423 for r in scmutil.revrange(self.repo, [hgrevs]):
427 for r in scmutil.revrange(self.repo, [hgrevs]):
424 ctx = self.repo[r]
428 ctx = self.repo[r]
425 nodes.add(ctx.node())
429 nodes.add(ctx.node())
426 parents.update(p.node() for p in ctx.parents())
430 parents.update(p.node() for p in ctx.parents())
427 self.keep = nodes.__contains__
431 self.keep = nodes.__contains__
428 self._heads = nodes - parents
432 self._heads = nodes - parents
429
433
430 def changectx(self, rev):
434 def changectx(self, rev):
431 if self.lastrev != rev:
435 if self.lastrev != rev:
432 self.lastctx = self.repo[rev]
436 self.lastctx = self.repo[rev]
433 self.lastrev = rev
437 self.lastrev = rev
434 return self.lastctx
438 return self.lastctx
435
439
436 def parents(self, ctx):
440 def parents(self, ctx):
437 return [p for p in ctx.parents() if p and self.keep(p.node())]
441 return [p for p in ctx.parents() if p and self.keep(p.node())]
438
442
439 def getheads(self):
443 def getheads(self):
440 return [hex(h) for h in self._heads if self.keep(h)]
444 return [hex(h) for h in self._heads if self.keep(h)]
441
445
442 def getfile(self, name, rev):
446 def getfile(self, name, rev):
443 try:
447 try:
444 fctx = self.changectx(rev)[name]
448 fctx = self.changectx(rev)[name]
445 return fctx.data(), fctx.flags()
449 return fctx.data(), fctx.flags()
446 except error.LookupError:
450 except error.LookupError:
447 return None, None
451 return None, None
448
452
449 def getchanges(self, rev, full):
453 def getchanges(self, rev, full):
450 ctx = self.changectx(rev)
454 ctx = self.changectx(rev)
451 parents = self.parents(ctx)
455 parents = self.parents(ctx)
452 if full or not parents:
456 if full or not parents:
453 files = copyfiles = ctx.manifest()
457 files = copyfiles = ctx.manifest()
454 if parents:
458 if parents:
455 if self._changescache[0] == rev:
459 if self._changescache[0] == rev:
456 m, a, r = self._changescache[1]
460 m, a, r = self._changescache[1]
457 else:
461 else:
458 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
462 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
459 if not full:
463 if not full:
460 files = m + a + r
464 files = m + a + r
461 copyfiles = m + a
465 copyfiles = m + a
462 # getcopies() is also run for roots and before filtering so missing
466 # getcopies() is also run for roots and before filtering so missing
463 # revlogs are detected early
467 # revlogs are detected early
464 copies = self.getcopies(ctx, parents, copyfiles)
468 copies = self.getcopies(ctx, parents, copyfiles)
465 cleanp2 = set()
469 cleanp2 = set()
466 if len(parents) == 2:
470 if len(parents) == 2:
467 cleanp2.update(self.repo.status(parents[1].node(), ctx.node(),
471 cleanp2.update(self.repo.status(parents[1].node(), ctx.node(),
468 clean=True).clean)
472 clean=True).clean)
469 changes = [(f, rev) for f in files if f not in self.ignored]
473 changes = [(f, rev) for f in files if f not in self.ignored]
470 changes.sort()
474 changes.sort()
471 return changes, copies, cleanp2
475 return changes, copies, cleanp2
472
476
473 def getcopies(self, ctx, parents, files):
477 def getcopies(self, ctx, parents, files):
474 copies = {}
478 copies = {}
475 for name in files:
479 for name in files:
476 if name in self.ignored:
480 if name in self.ignored:
477 continue
481 continue
478 try:
482 try:
479 copysource, _copynode = ctx.filectx(name).renamed()
483 copysource, _copynode = ctx.filectx(name).renamed()
480 if copysource in self.ignored:
484 if copysource in self.ignored:
481 continue
485 continue
482 # Ignore copy sources not in parent revisions
486 # Ignore copy sources not in parent revisions
483 found = False
487 found = False
484 for p in parents:
488 for p in parents:
485 if copysource in p:
489 if copysource in p:
486 found = True
490 found = True
487 break
491 break
488 if not found:
492 if not found:
489 continue
493 continue
490 copies[name] = copysource
494 copies[name] = copysource
491 except TypeError:
495 except TypeError:
492 pass
496 pass
493 except error.LookupError as e:
497 except error.LookupError as e:
494 if not self.ignoreerrors:
498 if not self.ignoreerrors:
495 raise
499 raise
496 self.ignored.add(name)
500 self.ignored.add(name)
497 self.ui.warn(_('ignoring: %s\n') % e)
501 self.ui.warn(_('ignoring: %s\n') % e)
498 return copies
502 return copies
499
503
500 def getcommit(self, rev):
504 def getcommit(self, rev):
501 ctx = self.changectx(rev)
505 ctx = self.changectx(rev)
502 parents = [p.hex() for p in self.parents(ctx)]
506 parents = [p.hex() for p in self.parents(ctx)]
503 crev = rev
507 crev = rev
504
508
505 return commit(author=ctx.user(),
509 return commit(author=ctx.user(),
506 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
510 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
507 desc=ctx.description(), rev=crev, parents=parents,
511 desc=ctx.description(), rev=crev, parents=parents,
508 branch=ctx.branch(), extra=ctx.extra(),
512 branch=ctx.branch(), extra=ctx.extra(),
509 sortkey=ctx.rev(), saverev=self.saverev,
513 sortkey=ctx.rev(), saverev=self.saverev,
510 phase=ctx.phase())
514 phase=ctx.phase())
511
515
512 def gettags(self):
516 def gettags(self):
513 # This will get written to .hgtags, filter non global tags out.
517 # This will get written to .hgtags, filter non global tags out.
514 tags = [t for t in self.repo.tagslist()
518 tags = [t for t in self.repo.tagslist()
515 if self.repo.tagtype(t[0]) == 'global']
519 if self.repo.tagtype(t[0]) == 'global']
516 return dict([(name, hex(node)) for name, node in tags
520 return dict([(name, hex(node)) for name, node in tags
517 if self.keep(node)])
521 if self.keep(node)])
518
522
519 def getchangedfiles(self, rev, i):
523 def getchangedfiles(self, rev, i):
520 ctx = self.changectx(rev)
524 ctx = self.changectx(rev)
521 parents = self.parents(ctx)
525 parents = self.parents(ctx)
522 if not parents and i is None:
526 if not parents and i is None:
523 i = 0
527 i = 0
524 changes = [], ctx.manifest().keys(), []
528 changes = [], ctx.manifest().keys(), []
525 else:
529 else:
526 i = i or 0
530 i = i or 0
527 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
531 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
528 changes = [[f for f in l if f not in self.ignored] for l in changes]
532 changes = [[f for f in l if f not in self.ignored] for l in changes]
529
533
530 if i == 0:
534 if i == 0:
531 self._changescache = (rev, changes)
535 self._changescache = (rev, changes)
532
536
533 return changes[0] + changes[1] + changes[2]
537 return changes[0] + changes[1] + changes[2]
534
538
535 def converted(self, rev, destrev):
539 def converted(self, rev, destrev):
536 if self.convertfp is None:
540 if self.convertfp is None:
537 self.convertfp = open(self.repo.join('shamap'), 'a')
541 self.convertfp = open(self.repo.join('shamap'), 'a')
538 self.convertfp.write('%s %s\n' % (destrev, rev))
542 self.convertfp.write('%s %s\n' % (destrev, rev))
539 self.convertfp.flush()
543 self.convertfp.flush()
540
544
541 def before(self):
545 def before(self):
542 self.ui.debug('run hg source pre-conversion action\n')
546 self.ui.debug('run hg source pre-conversion action\n')
543
547
544 def after(self):
548 def after(self):
545 self.ui.debug('run hg source post-conversion action\n')
549 self.ui.debug('run hg source post-conversion action\n')
546
550
547 def hasnativeorder(self):
551 def hasnativeorder(self):
548 return True
552 return True
549
553
550 def hasnativeclose(self):
554 def hasnativeclose(self):
551 return True
555 return True
552
556
553 def lookuprev(self, rev):
557 def lookuprev(self, rev):
554 try:
558 try:
555 return hex(self.repo.lookup(rev))
559 return hex(self.repo.lookup(rev))
556 except (error.RepoError, error.LookupError):
560 except (error.RepoError, error.LookupError):
557 return None
561 return None
558
562
559 def getbookmarks(self):
563 def getbookmarks(self):
560 return bookmarks.listbookmarks(self.repo)
564 return bookmarks.listbookmarks(self.repo)
561
565
562 def checkrevformat(self, revstr, mapname='splicemap'):
566 def checkrevformat(self, revstr, mapname='splicemap'):
563 """ Mercurial, revision string is a 40 byte hex """
567 """ Mercurial, revision string is a 40 byte hex """
564 self.checkhexformat(revstr, mapname)
568 self.checkhexformat(revstr, mapname)
@@ -1,505 +1,523 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > convert=
3 > convert=
4 > [convert]
4 > [convert]
5 > hg.saverev=False
5 > hg.saverev=False
6 > EOF
6 > EOF
7 $ hg help convert
7 $ hg help convert
8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
9
9
10 convert a foreign SCM repository to a Mercurial one.
10 convert a foreign SCM repository to a Mercurial one.
11
11
12 Accepted source formats [identifiers]:
12 Accepted source formats [identifiers]:
13
13
14 - Mercurial [hg]
14 - Mercurial [hg]
15 - CVS [cvs]
15 - CVS [cvs]
16 - Darcs [darcs]
16 - Darcs [darcs]
17 - git [git]
17 - git [git]
18 - Subversion [svn]
18 - Subversion [svn]
19 - Monotone [mtn]
19 - Monotone [mtn]
20 - GNU Arch [gnuarch]
20 - GNU Arch [gnuarch]
21 - Bazaar [bzr]
21 - Bazaar [bzr]
22 - Perforce [p4]
22 - Perforce [p4]
23
23
24 Accepted destination formats [identifiers]:
24 Accepted destination formats [identifiers]:
25
25
26 - Mercurial [hg]
26 - Mercurial [hg]
27 - Subversion [svn] (history on branches is not preserved)
27 - Subversion [svn] (history on branches is not preserved)
28
28
29 If no revision is given, all revisions will be converted. Otherwise,
29 If no revision is given, all revisions will be converted. Otherwise,
30 convert will only import up to the named revision (given in a format
30 convert will only import up to the named revision (given in a format
31 understood by the source).
31 understood by the source).
32
32
33 If no destination directory name is specified, it defaults to the basename
33 If no destination directory name is specified, it defaults to the basename
34 of the source with "-hg" appended. If the destination repository doesn't
34 of the source with "-hg" appended. If the destination repository doesn't
35 exist, it will be created.
35 exist, it will be created.
36
36
37 By default, all sources except Mercurial will use --branchsort. Mercurial
37 By default, all sources except Mercurial will use --branchsort. Mercurial
38 uses --sourcesort to preserve original revision numbers order. Sort modes
38 uses --sourcesort to preserve original revision numbers order. Sort modes
39 have the following effects:
39 have the following effects:
40
40
41 --branchsort convert from parent to child revision when possible, which
41 --branchsort convert from parent to child revision when possible, which
42 means branches are usually converted one after the other.
42 means branches are usually converted one after the other.
43 It generates more compact repositories.
43 It generates more compact repositories.
44 --datesort sort revisions by date. Converted repositories have good-
44 --datesort sort revisions by date. Converted repositories have good-
45 looking changelogs but are often an order of magnitude
45 looking changelogs but are often an order of magnitude
46 larger than the same ones generated by --branchsort.
46 larger than the same ones generated by --branchsort.
47 --sourcesort try to preserve source revisions order, only supported by
47 --sourcesort try to preserve source revisions order, only supported by
48 Mercurial sources.
48 Mercurial sources.
49 --closesort try to move closed revisions as close as possible to parent
49 --closesort try to move closed revisions as close as possible to parent
50 branches, only supported by Mercurial sources.
50 branches, only supported by Mercurial sources.
51
51
52 If "REVMAP" isn't given, it will be put in a default location
52 If "REVMAP" isn't given, it will be put in a default location
53 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
53 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
54 maps each source commit ID to the destination ID for that revision, like
54 maps each source commit ID to the destination ID for that revision, like
55 so:
55 so:
56
56
57 <source ID> <destination ID>
57 <source ID> <destination ID>
58
58
59 If the file doesn't exist, it's automatically created. It's updated on
59 If the file doesn't exist, it's automatically created. It's updated on
60 each commit copied, so "hg convert" can be interrupted and can be run
60 each commit copied, so "hg convert" can be interrupted and can be run
61 repeatedly to copy new commits.
61 repeatedly to copy new commits.
62
62
63 The authormap is a simple text file that maps each source commit author to
63 The authormap is a simple text file that maps each source commit author to
64 a destination commit author. It is handy for source SCMs that use unix
64 a destination commit author. It is handy for source SCMs that use unix
65 logins to identify authors (e.g.: CVS). One line per author mapping and
65 logins to identify authors (e.g.: CVS). One line per author mapping and
66 the line format is:
66 the line format is:
67
67
68 source author = destination author
68 source author = destination author
69
69
70 Empty lines and lines starting with a "#" are ignored.
70 Empty lines and lines starting with a "#" are ignored.
71
71
72 The filemap is a file that allows filtering and remapping of files and
72 The filemap is a file that allows filtering and remapping of files and
73 directories. Each line can contain one of the following directives:
73 directories. Each line can contain one of the following directives:
74
74
75 include path/to/file-or-dir
75 include path/to/file-or-dir
76
76
77 exclude path/to/file-or-dir
77 exclude path/to/file-or-dir
78
78
79 rename path/to/source path/to/destination
79 rename path/to/source path/to/destination
80
80
81 Comment lines start with "#". A specified path matches if it equals the
81 Comment lines start with "#". A specified path matches if it equals the
82 full relative name of a file or one of its parent directories. The
82 full relative name of a file or one of its parent directories. The
83 "include" or "exclude" directive with the longest matching path applies,
83 "include" or "exclude" directive with the longest matching path applies,
84 so line order does not matter.
84 so line order does not matter.
85
85
86 The "include" directive causes a file, or all files under a directory, to
86 The "include" directive causes a file, or all files under a directory, to
87 be included in the destination repository. The default if there are no
87 be included in the destination repository. The default if there are no
88 "include" statements is to include everything. If there are any "include"
88 "include" statements is to include everything. If there are any "include"
89 statements, nothing else is included. The "exclude" directive causes files
89 statements, nothing else is included. The "exclude" directive causes files
90 or directories to be omitted. The "rename" directive renames a file or
90 or directories to be omitted. The "rename" directive renames a file or
91 directory if it is converted. To rename from a subdirectory into the root
91 directory if it is converted. To rename from a subdirectory into the root
92 of the repository, use "." as the path to rename to.
92 of the repository, use "." as the path to rename to.
93
93
94 "--full" will make sure the converted changesets contain exactly the right
94 "--full" will make sure the converted changesets contain exactly the right
95 files with the right content. It will make a full conversion of all files,
95 files with the right content. It will make a full conversion of all files,
96 not just the ones that have changed. Files that already are correct will
96 not just the ones that have changed. Files that already are correct will
97 not be changed. This can be used to apply filemap changes when converting
97 not be changed. This can be used to apply filemap changes when converting
98 incrementally. This is currently only supported for Mercurial and
98 incrementally. This is currently only supported for Mercurial and
99 Subversion.
99 Subversion.
100
100
101 The splicemap is a file that allows insertion of synthetic history,
101 The splicemap is a file that allows insertion of synthetic history,
102 letting you specify the parents of a revision. This is useful if you want
102 letting you specify the parents of a revision. This is useful if you want
103 to e.g. give a Subversion merge two parents, or graft two disconnected
103 to e.g. give a Subversion merge two parents, or graft two disconnected
104 series of history together. Each entry contains a key, followed by a
104 series of history together. Each entry contains a key, followed by a
105 space, followed by one or two comma-separated values:
105 space, followed by one or two comma-separated values:
106
106
107 key parent1, parent2
107 key parent1, parent2
108
108
109 The key is the revision ID in the source revision control system whose
109 The key is the revision ID in the source revision control system whose
110 parents should be modified (same format as a key in .hg/shamap). The
110 parents should be modified (same format as a key in .hg/shamap). The
111 values are the revision IDs (in either the source or destination revision
111 values are the revision IDs (in either the source or destination revision
112 control system) that should be used as the new parents for that node. For
112 control system) that should be used as the new parents for that node. For
113 example, if you have merged "release-1.0" into "trunk", then you should
113 example, if you have merged "release-1.0" into "trunk", then you should
114 specify the revision on "trunk" as the first parent and the one on the
114 specify the revision on "trunk" as the first parent and the one on the
115 "release-1.0" branch as the second.
115 "release-1.0" branch as the second.
116
116
117 The branchmap is a file that allows you to rename a branch when it is
117 The branchmap is a file that allows you to rename a branch when it is
118 being brought in from whatever external repository. When used in
118 being brought in from whatever external repository. When used in
119 conjunction with a splicemap, it allows for a powerful combination to help
119 conjunction with a splicemap, it allows for a powerful combination to help
120 fix even the most badly mismanaged repositories and turn them into nicely
120 fix even the most badly mismanaged repositories and turn them into nicely
121 structured Mercurial repositories. The branchmap contains lines of the
121 structured Mercurial repositories. The branchmap contains lines of the
122 form:
122 form:
123
123
124 original_branch_name new_branch_name
124 original_branch_name new_branch_name
125
125
126 where "original_branch_name" is the name of the branch in the source
126 where "original_branch_name" is the name of the branch in the source
127 repository, and "new_branch_name" is the name of the branch is the
127 repository, and "new_branch_name" is the name of the branch is the
128 destination repository. No whitespace is allowed in the branch names. This
128 destination repository. No whitespace is allowed in the branch names. This
129 can be used to (for instance) move code in one repository from "default"
129 can be used to (for instance) move code in one repository from "default"
130 to a named branch.
130 to a named branch.
131
131
132 Mercurial Source
132 Mercurial Source
133 ################
133 ################
134
134
135 The Mercurial source recognizes the following configuration options, which
135 The Mercurial source recognizes the following configuration options, which
136 you can set on the command line with "--config":
136 you can set on the command line with "--config":
137
137
138 convert.hg.ignoreerrors
138 convert.hg.ignoreerrors
139 ignore integrity errors when reading. Use it to fix
139 ignore integrity errors when reading. Use it to fix
140 Mercurial repositories with missing revlogs, by converting
140 Mercurial repositories with missing revlogs, by converting
141 from and to Mercurial. Default is False.
141 from and to Mercurial. Default is False.
142 convert.hg.saverev
142 convert.hg.saverev
143 store original revision ID in changeset (forces target IDs
143 store original revision ID in changeset (forces target IDs
144 to change). It takes a boolean argument and defaults to
144 to change). It takes a boolean argument and defaults to
145 False.
145 False.
146 convert.hg.revs
146 convert.hg.revs
147 revset specifying the source revisions to convert.
147 revset specifying the source revisions to convert.
148
148
149 CVS Source
149 CVS Source
150 ##########
150 ##########
151
151
152 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
152 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
153 indicate the starting point of what will be converted. Direct access to
153 indicate the starting point of what will be converted. Direct access to
154 the repository files is not needed, unless of course the repository is
154 the repository files is not needed, unless of course the repository is
155 ":local:". The conversion uses the top level directory in the sandbox to
155 ":local:". The conversion uses the top level directory in the sandbox to
156 find the CVS repository, and then uses CVS rlog commands to find files to
156 find the CVS repository, and then uses CVS rlog commands to find files to
157 convert. This means that unless a filemap is given, all files under the
157 convert. This means that unless a filemap is given, all files under the
158 starting directory will be converted, and that any directory
158 starting directory will be converted, and that any directory
159 reorganization in the CVS sandbox is ignored.
159 reorganization in the CVS sandbox is ignored.
160
160
161 The following options can be used with "--config":
161 The following options can be used with "--config":
162
162
163 convert.cvsps.cache
163 convert.cvsps.cache
164 Set to False to disable remote log caching, for testing and
164 Set to False to disable remote log caching, for testing and
165 debugging purposes. Default is True.
165 debugging purposes. Default is True.
166 convert.cvsps.fuzz
166 convert.cvsps.fuzz
167 Specify the maximum time (in seconds) that is allowed
167 Specify the maximum time (in seconds) that is allowed
168 between commits with identical user and log message in a
168 between commits with identical user and log message in a
169 single changeset. When very large files were checked in as
169 single changeset. When very large files were checked in as
170 part of a changeset then the default may not be long enough.
170 part of a changeset then the default may not be long enough.
171 The default is 60.
171 The default is 60.
172 convert.cvsps.mergeto
172 convert.cvsps.mergeto
173 Specify a regular expression to which commit log messages
173 Specify a regular expression to which commit log messages
174 are matched. If a match occurs, then the conversion process
174 are matched. If a match occurs, then the conversion process
175 will insert a dummy revision merging the branch on which
175 will insert a dummy revision merging the branch on which
176 this log message occurs to the branch indicated in the
176 this log message occurs to the branch indicated in the
177 regex. Default is "{{mergetobranch ([-\w]+)}}"
177 regex. Default is "{{mergetobranch ([-\w]+)}}"
178 convert.cvsps.mergefrom
178 convert.cvsps.mergefrom
179 Specify a regular expression to which commit log messages
179 Specify a regular expression to which commit log messages
180 are matched. If a match occurs, then the conversion process
180 are matched. If a match occurs, then the conversion process
181 will add the most recent revision on the branch indicated in
181 will add the most recent revision on the branch indicated in
182 the regex as the second parent of the changeset. Default is
182 the regex as the second parent of the changeset. Default is
183 "{{mergefrombranch ([-\w]+)}}"
183 "{{mergefrombranch ([-\w]+)}}"
184 convert.localtimezone
184 convert.localtimezone
185 use local time (as determined by the TZ environment
185 use local time (as determined by the TZ environment
186 variable) for changeset date/times. The default is False
186 variable) for changeset date/times. The default is False
187 (use UTC).
187 (use UTC).
188 hooks.cvslog Specify a Python function to be called at the end of
188 hooks.cvslog Specify a Python function to be called at the end of
189 gathering the CVS log. The function is passed a list with
189 gathering the CVS log. The function is passed a list with
190 the log entries, and can modify the entries in-place, or add
190 the log entries, and can modify the entries in-place, or add
191 or delete them.
191 or delete them.
192 hooks.cvschangesets
192 hooks.cvschangesets
193 Specify a Python function to be called after the changesets
193 Specify a Python function to be called after the changesets
194 are calculated from the CVS log. The function is passed a
194 are calculated from the CVS log. The function is passed a
195 list with the changeset entries, and can modify the
195 list with the changeset entries, and can modify the
196 changesets in-place, or add or delete them.
196 changesets in-place, or add or delete them.
197
197
198 An additional "debugcvsps" Mercurial command allows the builtin changeset
198 An additional "debugcvsps" Mercurial command allows the builtin changeset
199 merging code to be run without doing a conversion. Its parameters and
199 merging code to be run without doing a conversion. Its parameters and
200 output are similar to that of cvsps 2.1. Please see the command help for
200 output are similar to that of cvsps 2.1. Please see the command help for
201 more details.
201 more details.
202
202
203 Subversion Source
203 Subversion Source
204 #################
204 #################
205
205
206 Subversion source detects classical trunk/branches/tags layouts. By
206 Subversion source detects classical trunk/branches/tags layouts. By
207 default, the supplied "svn://repo/path/" source URL is converted as a
207 default, the supplied "svn://repo/path/" source URL is converted as a
208 single branch. If "svn://repo/path/trunk" exists it replaces the default
208 single branch. If "svn://repo/path/trunk" exists it replaces the default
209 branch. If "svn://repo/path/branches" exists, its subdirectories are
209 branch. If "svn://repo/path/branches" exists, its subdirectories are
210 listed as possible branches. If "svn://repo/path/tags" exists, it is
210 listed as possible branches. If "svn://repo/path/tags" exists, it is
211 looked for tags referencing converted branches. Default "trunk",
211 looked for tags referencing converted branches. Default "trunk",
212 "branches" and "tags" values can be overridden with following options. Set
212 "branches" and "tags" values can be overridden with following options. Set
213 them to paths relative to the source URL, or leave them blank to disable
213 them to paths relative to the source URL, or leave them blank to disable
214 auto detection.
214 auto detection.
215
215
216 The following options can be set with "--config":
216 The following options can be set with "--config":
217
217
218 convert.svn.branches
218 convert.svn.branches
219 specify the directory containing branches. The default is
219 specify the directory containing branches. The default is
220 "branches".
220 "branches".
221 convert.svn.tags
221 convert.svn.tags
222 specify the directory containing tags. The default is
222 specify the directory containing tags. The default is
223 "tags".
223 "tags".
224 convert.svn.trunk
224 convert.svn.trunk
225 specify the name of the trunk branch. The default is
225 specify the name of the trunk branch. The default is
226 "trunk".
226 "trunk".
227 convert.localtimezone
227 convert.localtimezone
228 use local time (as determined by the TZ environment
228 use local time (as determined by the TZ environment
229 variable) for changeset date/times. The default is False
229 variable) for changeset date/times. The default is False
230 (use UTC).
230 (use UTC).
231
231
232 Source history can be retrieved starting at a specific revision, instead
232 Source history can be retrieved starting at a specific revision, instead
233 of being integrally converted. Only single branch conversions are
233 of being integrally converted. Only single branch conversions are
234 supported.
234 supported.
235
235
236 convert.svn.startrev
236 convert.svn.startrev
237 specify start Subversion revision number. The default is 0.
237 specify start Subversion revision number. The default is 0.
238
238
239 Git Source
239 Git Source
240 ##########
240 ##########
241
241
242 The Git importer converts commits from all reachable branches (refs in
242 The Git importer converts commits from all reachable branches (refs in
243 refs/heads) and remotes (refs in refs/remotes) to Mercurial. Branches are
243 refs/heads) and remotes (refs in refs/remotes) to Mercurial. Branches are
244 converted to bookmarks with the same name, with the leading 'refs/heads'
244 converted to bookmarks with the same name, with the leading 'refs/heads'
245 stripped. Git submodules are converted to Git subrepos in Mercurial.
245 stripped. Git submodules are converted to Git subrepos in Mercurial.
246
246
247 The following options can be set with "--config":
247 The following options can be set with "--config":
248
248
249 convert.git.similarity
249 convert.git.similarity
250 specify how similar files modified in a commit must be to be
250 specify how similar files modified in a commit must be to be
251 imported as renames or copies, as a percentage between "0"
251 imported as renames or copies, as a percentage between "0"
252 (disabled) and "100" (files must be identical). For example,
252 (disabled) and "100" (files must be identical). For example,
253 "90" means that a delete/add pair will be imported as a
253 "90" means that a delete/add pair will be imported as a
254 rename if more than 90% of the file hasn't changed. The
254 rename if more than 90% of the file hasn't changed. The
255 default is "50".
255 default is "50".
256 convert.git.findcopiesharder
256 convert.git.findcopiesharder
257 while detecting copies, look at all files in the working
257 while detecting copies, look at all files in the working
258 copy instead of just changed ones. This is very expensive
258 copy instead of just changed ones. This is very expensive
259 for large projects, and is only effective when
259 for large projects, and is only effective when
260 "convert.git.similarity" is greater than 0. The default is
260 "convert.git.similarity" is greater than 0. The default is
261 False.
261 False.
262
262
263 Perforce Source
263 Perforce Source
264 ###############
264 ###############
265
265
266 The Perforce (P4) importer can be given a p4 depot path or a client
266 The Perforce (P4) importer can be given a p4 depot path or a client
267 specification as source. It will convert all files in the source to a flat
267 specification as source. It will convert all files in the source to a flat
268 Mercurial repository, ignoring labels, branches and integrations. Note
268 Mercurial repository, ignoring labels, branches and integrations. Note
269 that when a depot path is given you then usually should specify a target
269 that when a depot path is given you then usually should specify a target
270 directory, because otherwise the target may be named "...-hg".
270 directory, because otherwise the target may be named "...-hg".
271
271
272 It is possible to limit the amount of source history to be converted by
272 It is possible to limit the amount of source history to be converted by
273 specifying an initial Perforce revision:
273 specifying an initial Perforce revision:
274
274
275 convert.p4.startrev
275 convert.p4.startrev
276 specify initial Perforce revision (a Perforce changelist
276 specify initial Perforce revision (a Perforce changelist
277 number).
277 number).
278
278
279 Mercurial Destination
279 Mercurial Destination
280 #####################
280 #####################
281
281
282 The Mercurial destination will recognize Mercurial subrepositories in the
282 The Mercurial destination will recognize Mercurial subrepositories in the
283 destination directory, and update the .hgsubstate file automatically if
283 destination directory, and update the .hgsubstate file automatically if
284 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
284 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
285 Converting a repository with subrepositories requires converting a single
285 Converting a repository with subrepositories requires converting a single
286 repository at a time, from the bottom up.
286 repository at a time, from the bottom up.
287
287
288 The following options are supported:
288 The following options are supported:
289
289
290 convert.hg.clonebranches
290 convert.hg.clonebranches
291 dispatch source branches in separate clones. The default is
291 dispatch source branches in separate clones. The default is
292 False.
292 False.
293 convert.hg.tagsbranch
293 convert.hg.tagsbranch
294 branch name for tag revisions, defaults to "default".
294 branch name for tag revisions, defaults to "default".
295 convert.hg.usebranchnames
295 convert.hg.usebranchnames
296 preserve branch names. The default is True.
296 preserve branch names. The default is True.
297 convert.hg.sourcename
298 records the given string as a 'convert_source' extra value
299 on each commit made in the target repository. The default is
300 None.
297
301
298 All Destinations
302 All Destinations
299 ################
303 ################
300
304
301 All destination types accept the following options:
305 All destination types accept the following options:
302
306
303 convert.skiptags
307 convert.skiptags
304 does not convert tags from the source repo to the target
308 does not convert tags from the source repo to the target
305 repo. The default is False.
309 repo. The default is False.
306
310
307 options ([+] can be repeated):
311 options ([+] can be repeated):
308
312
309 -s --source-type TYPE source repository type
313 -s --source-type TYPE source repository type
310 -d --dest-type TYPE destination repository type
314 -d --dest-type TYPE destination repository type
311 -r --rev REV [+] import up to source revision REV
315 -r --rev REV [+] import up to source revision REV
312 -A --authormap FILE remap usernames using this file
316 -A --authormap FILE remap usernames using this file
313 --filemap FILE remap file names using contents of file
317 --filemap FILE remap file names using contents of file
314 --full apply filemap changes by converting all files again
318 --full apply filemap changes by converting all files again
315 --splicemap FILE splice synthesized history into place
319 --splicemap FILE splice synthesized history into place
316 --branchmap FILE change branch names while converting
320 --branchmap FILE change branch names while converting
317 --branchsort try to sort changesets by branches
321 --branchsort try to sort changesets by branches
318 --datesort try to sort changesets by date
322 --datesort try to sort changesets by date
319 --sourcesort preserve source changesets order
323 --sourcesort preserve source changesets order
320 --closesort try to reorder closed revisions
324 --closesort try to reorder closed revisions
321
325
322 (some details hidden, use --verbose to show complete help)
326 (some details hidden, use --verbose to show complete help)
323 $ hg init a
327 $ hg init a
324 $ cd a
328 $ cd a
325 $ echo a > a
329 $ echo a > a
326 $ hg ci -d'0 0' -Ama
330 $ hg ci -d'0 0' -Ama
327 adding a
331 adding a
328 $ hg cp a b
332 $ hg cp a b
329 $ hg ci -d'1 0' -mb
333 $ hg ci -d'1 0' -mb
330 $ hg rm a
334 $ hg rm a
331 $ hg ci -d'2 0' -mc
335 $ hg ci -d'2 0' -mc
332 $ hg mv b a
336 $ hg mv b a
333 $ hg ci -d'3 0' -md
337 $ hg ci -d'3 0' -md
334 $ echo a >> a
338 $ echo a >> a
335 $ hg ci -d'4 0' -me
339 $ hg ci -d'4 0' -me
336 $ cd ..
340 $ cd ..
337 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
341 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
338 assuming destination a-hg
342 assuming destination a-hg
339 initializing destination a-hg repository
343 initializing destination a-hg repository
340 scanning source...
344 scanning source...
341 sorting...
345 sorting...
342 converting...
346 converting...
343 4 a
347 4 a
344 3 b
348 3 b
345 2 c
349 2 c
346 1 d
350 1 d
347 0 e
351 0 e
348 $ hg --cwd a-hg pull ../a
352 $ hg --cwd a-hg pull ../a
349 pulling from ../a
353 pulling from ../a
350 searching for changes
354 searching for changes
351 no changes found
355 no changes found
352
356
353 conversion to existing file should fail
357 conversion to existing file should fail
354
358
355 $ touch bogusfile
359 $ touch bogusfile
356 $ hg convert a bogusfile
360 $ hg convert a bogusfile
357 initializing destination bogusfile repository
361 initializing destination bogusfile repository
358 abort: cannot create new bundle repository
362 abort: cannot create new bundle repository
359 [255]
363 [255]
360
364
361 #if unix-permissions no-root
365 #if unix-permissions no-root
362
366
363 conversion to dir without permissions should fail
367 conversion to dir without permissions should fail
364
368
365 $ mkdir bogusdir
369 $ mkdir bogusdir
366 $ chmod 000 bogusdir
370 $ chmod 000 bogusdir
367
371
368 $ hg convert a bogusdir
372 $ hg convert a bogusdir
369 abort: Permission denied: 'bogusdir'
373 abort: Permission denied: 'bogusdir'
370 [255]
374 [255]
371
375
372 user permissions should succeed
376 user permissions should succeed
373
377
374 $ chmod 700 bogusdir
378 $ chmod 700 bogusdir
375 $ hg convert a bogusdir
379 $ hg convert a bogusdir
376 initializing destination bogusdir repository
380 initializing destination bogusdir repository
377 scanning source...
381 scanning source...
378 sorting...
382 sorting...
379 converting...
383 converting...
380 4 a
384 4 a
381 3 b
385 3 b
382 2 c
386 2 c
383 1 d
387 1 d
384 0 e
388 0 e
385
389
386 #endif
390 #endif
387
391
388 test pre and post conversion actions
392 test pre and post conversion actions
389
393
390 $ echo 'include b' > filemap
394 $ echo 'include b' > filemap
391 $ hg convert --debug --filemap filemap a partialb | \
395 $ hg convert --debug --filemap filemap a partialb | \
392 > grep 'run hg'
396 > grep 'run hg'
393 run hg source pre-conversion action
397 run hg source pre-conversion action
394 run hg sink pre-conversion action
398 run hg sink pre-conversion action
395 run hg sink post-conversion action
399 run hg sink post-conversion action
396 run hg source post-conversion action
400 run hg source post-conversion action
397
401
398 converting empty dir should fail "nicely
402 converting empty dir should fail "nicely
399
403
400 $ mkdir emptydir
404 $ mkdir emptydir
401
405
402 override $PATH to ensure p4 not visible; use $PYTHON in case we're
406 override $PATH to ensure p4 not visible; use $PYTHON in case we're
403 running from a devel copy, not a temp installation
407 running from a devel copy, not a temp installation
404
408
405 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
409 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
406 assuming destination emptydir-hg
410 assuming destination emptydir-hg
407 initializing destination emptydir-hg repository
411 initializing destination emptydir-hg repository
408 emptydir does not look like a CVS checkout
412 emptydir does not look like a CVS checkout
409 emptydir does not look like a Git repository
413 emptydir does not look like a Git repository
410 emptydir does not look like a Subversion repository
414 emptydir does not look like a Subversion repository
411 emptydir is not a local Mercurial repository
415 emptydir is not a local Mercurial repository
412 emptydir does not look like a darcs repository
416 emptydir does not look like a darcs repository
413 emptydir does not look like a monotone repository
417 emptydir does not look like a monotone repository
414 emptydir does not look like a GNU Arch repository
418 emptydir does not look like a GNU Arch repository
415 emptydir does not look like a Bazaar repository
419 emptydir does not look like a Bazaar repository
416 cannot find required "p4" tool
420 cannot find required "p4" tool
417 abort: emptydir: missing or unsupported repository
421 abort: emptydir: missing or unsupported repository
418 [255]
422 [255]
419
423
420 convert with imaginary source type
424 convert with imaginary source type
421
425
422 $ hg convert --source-type foo a a-foo
426 $ hg convert --source-type foo a a-foo
423 initializing destination a-foo repository
427 initializing destination a-foo repository
424 abort: foo: invalid source repository type
428 abort: foo: invalid source repository type
425 [255]
429 [255]
426
430
427 convert with imaginary sink type
431 convert with imaginary sink type
428
432
429 $ hg convert --dest-type foo a a-foo
433 $ hg convert --dest-type foo a a-foo
430 abort: foo: invalid destination repository type
434 abort: foo: invalid destination repository type
431 [255]
435 [255]
432
436
433 testing: convert must not produce duplicate entries in fncache
437 testing: convert must not produce duplicate entries in fncache
434
438
435 $ hg convert a b
439 $ hg convert a b
436 initializing destination b repository
440 initializing destination b repository
437 scanning source...
441 scanning source...
438 sorting...
442 sorting...
439 converting...
443 converting...
440 4 a
444 4 a
441 3 b
445 3 b
442 2 c
446 2 c
443 1 d
447 1 d
444 0 e
448 0 e
445
449
446 contents of fncache file:
450 contents of fncache file:
447
451
448 $ cat b/.hg/store/fncache | sort
452 $ cat b/.hg/store/fncache | sort
449 data/a.i
453 data/a.i
450 data/b.i
454 data/b.i
451
455
452 test bogus URL
456 test bogus URL
453
457
454 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
458 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
455 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
459 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
456 [255]
460 [255]
457
461
458 test revset converted() lookup
462 test revset converted() lookup
459
463
460 $ hg --config convert.hg.saverev=True convert a c
464 $ hg --config convert.hg.saverev=True convert a c
461 initializing destination c repository
465 initializing destination c repository
462 scanning source...
466 scanning source...
463 sorting...
467 sorting...
464 converting...
468 converting...
465 4 a
469 4 a
466 3 b
470 3 b
467 2 c
471 2 c
468 1 d
472 1 d
469 0 e
473 0 e
470 $ echo f > c/f
474 $ echo f > c/f
471 $ hg -R c ci -d'0 0' -Amf
475 $ hg -R c ci -d'0 0' -Amf
472 adding f
476 adding f
473 created new head
477 created new head
474 $ hg -R c log -r "converted(09d945a62ce6)"
478 $ hg -R c log -r "converted(09d945a62ce6)"
475 changeset: 1:98c3dd46a874
479 changeset: 1:98c3dd46a874
476 user: test
480 user: test
477 date: Thu Jan 01 00:00:01 1970 +0000
481 date: Thu Jan 01 00:00:01 1970 +0000
478 summary: b
482 summary: b
479
483
480 $ hg -R c log -r "converted()"
484 $ hg -R c log -r "converted()"
481 changeset: 0:31ed57b2037c
485 changeset: 0:31ed57b2037c
482 user: test
486 user: test
483 date: Thu Jan 01 00:00:00 1970 +0000
487 date: Thu Jan 01 00:00:00 1970 +0000
484 summary: a
488 summary: a
485
489
486 changeset: 1:98c3dd46a874
490 changeset: 1:98c3dd46a874
487 user: test
491 user: test
488 date: Thu Jan 01 00:00:01 1970 +0000
492 date: Thu Jan 01 00:00:01 1970 +0000
489 summary: b
493 summary: b
490
494
491 changeset: 2:3b9ca06ef716
495 changeset: 2:3b9ca06ef716
492 user: test
496 user: test
493 date: Thu Jan 01 00:00:02 1970 +0000
497 date: Thu Jan 01 00:00:02 1970 +0000
494 summary: c
498 summary: c
495
499
496 changeset: 3:4e0debd37cf2
500 changeset: 3:4e0debd37cf2
497 user: test
501 user: test
498 date: Thu Jan 01 00:00:03 1970 +0000
502 date: Thu Jan 01 00:00:03 1970 +0000
499 summary: d
503 summary: d
500
504
501 changeset: 4:9de3bc9349c5
505 changeset: 4:9de3bc9349c5
502 user: test
506 user: test
503 date: Thu Jan 01 00:00:04 1970 +0000
507 date: Thu Jan 01 00:00:04 1970 +0000
504 summary: e
508 summary: e
505
509
510
511 test specifying a sourcename
512 $ echo g > a/g
513 $ hg -R a ci -d'0 0' -Amg
514 adding g
515 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
516 scanning source...
517 sorting...
518 converting...
519 0 g
520 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
521 branch=default
522 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
523 convert_source=mysource
General Comments 0
You need to be logged in to leave comments. Login now