##// END OF EJS Templates
convert: add convert.git.skipsubmodules option...
Durham Goode -
r26077:e63d05fb default
parent child Browse files
Show More
@@ -1,447 +1,450 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.startrev: specify the initial Mercurial revision.
195 :convert.hg.startrev: specify the initial Mercurial revision.
196 The default is 0.
196 The default is 0.
197
197
198 :convert.hg.revs: revset specifying the source revisions to convert.
198 :convert.hg.revs: revset specifying the source revisions to convert.
199
199
200 CVS Source
200 CVS Source
201 ##########
201 ##########
202
202
203 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
203 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
204 to indicate the starting point of what will be converted. Direct
204 to indicate the starting point of what will be converted. Direct
205 access to the repository files is not needed, unless of course the
205 access to the repository files is not needed, unless of course the
206 repository is ``:local:``. The conversion uses the top level
206 repository is ``:local:``. The conversion uses the top level
207 directory in the sandbox to find the CVS repository, and then uses
207 directory in the sandbox to find the CVS repository, and then uses
208 CVS rlog commands to find files to convert. This means that unless
208 CVS rlog commands to find files to convert. This means that unless
209 a filemap is given, all files under the starting directory will be
209 a filemap is given, all files under the starting directory will be
210 converted, and that any directory reorganization in the CVS
210 converted, and that any directory reorganization in the CVS
211 sandbox is ignored.
211 sandbox is ignored.
212
212
213 The following options can be used with ``--config``:
213 The following options can be used with ``--config``:
214
214
215 :convert.cvsps.cache: Set to False to disable remote log caching,
215 :convert.cvsps.cache: Set to False to disable remote log caching,
216 for testing and debugging purposes. Default is True.
216 for testing and debugging purposes. Default is True.
217
217
218 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
218 :convert.cvsps.fuzz: Specify the maximum time (in seconds) that is
219 allowed between commits with identical user and log message in
219 allowed between commits with identical user and log message in
220 a single changeset. When very large files were checked in as
220 a single changeset. When very large files were checked in as
221 part of a changeset then the default may not be long enough.
221 part of a changeset then the default may not be long enough.
222 The default is 60.
222 The default is 60.
223
223
224 :convert.cvsps.mergeto: Specify a regular expression to which
224 :convert.cvsps.mergeto: Specify a regular expression to which
225 commit log messages are matched. If a match occurs, then the
225 commit log messages are matched. If a match occurs, then the
226 conversion process will insert a dummy revision merging the
226 conversion process will insert a dummy revision merging the
227 branch on which this log message occurs to the branch
227 branch on which this log message occurs to the branch
228 indicated in the regex. Default is ``{{mergetobranch
228 indicated in the regex. Default is ``{{mergetobranch
229 ([-\\w]+)}}``
229 ([-\\w]+)}}``
230
230
231 :convert.cvsps.mergefrom: Specify a regular expression to which
231 :convert.cvsps.mergefrom: Specify a regular expression to which
232 commit log messages are matched. If a match occurs, then the
232 commit log messages are matched. If a match occurs, then the
233 conversion process will add the most recent revision on the
233 conversion process will add the most recent revision on the
234 branch indicated in the regex as the second parent of the
234 branch indicated in the regex as the second parent of the
235 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
235 changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
236
236
237 :convert.localtimezone: use local time (as determined by the TZ
237 :convert.localtimezone: use local time (as determined by the TZ
238 environment variable) for changeset date/times. The default
238 environment variable) for changeset date/times. The default
239 is False (use UTC).
239 is False (use UTC).
240
240
241 :hooks.cvslog: Specify a Python function to be called at the end of
241 :hooks.cvslog: Specify a Python function to be called at the end of
242 gathering the CVS log. The function is passed a list with the
242 gathering the CVS log. The function is passed a list with the
243 log entries, and can modify the entries in-place, or add or
243 log entries, and can modify the entries in-place, or add or
244 delete them.
244 delete them.
245
245
246 :hooks.cvschangesets: Specify a Python function to be called after
246 :hooks.cvschangesets: Specify a Python function to be called after
247 the changesets are calculated from the CVS log. The
247 the changesets are calculated from the CVS log. The
248 function is passed a list with the changeset entries, and can
248 function is passed a list with the changeset entries, and can
249 modify the changesets in-place, or add or delete them.
249 modify the changesets in-place, or add or delete them.
250
250
251 An additional "debugcvsps" Mercurial command allows the builtin
251 An additional "debugcvsps" Mercurial command allows the builtin
252 changeset merging code to be run without doing a conversion. Its
252 changeset merging code to be run without doing a conversion. Its
253 parameters and output are similar to that of cvsps 2.1. Please see
253 parameters and output are similar to that of cvsps 2.1. Please see
254 the command help for more details.
254 the command help for more details.
255
255
256 Subversion Source
256 Subversion Source
257 #################
257 #################
258
258
259 Subversion source detects classical trunk/branches/tags layouts.
259 Subversion source detects classical trunk/branches/tags layouts.
260 By default, the supplied ``svn://repo/path/`` source URL is
260 By default, the supplied ``svn://repo/path/`` source URL is
261 converted as a single branch. If ``svn://repo/path/trunk`` exists
261 converted as a single branch. If ``svn://repo/path/trunk`` exists
262 it replaces the default branch. If ``svn://repo/path/branches``
262 it replaces the default branch. If ``svn://repo/path/branches``
263 exists, its subdirectories are listed as possible branches. If
263 exists, its subdirectories are listed as possible branches. If
264 ``svn://repo/path/tags`` exists, it is looked for tags referencing
264 ``svn://repo/path/tags`` exists, it is looked for tags referencing
265 converted branches. Default ``trunk``, ``branches`` and ``tags``
265 converted branches. Default ``trunk``, ``branches`` and ``tags``
266 values can be overridden with following options. Set them to paths
266 values can be overridden with following options. Set them to paths
267 relative to the source URL, or leave them blank to disable auto
267 relative to the source URL, or leave them blank to disable auto
268 detection.
268 detection.
269
269
270 The following options can be set with ``--config``:
270 The following options can be set with ``--config``:
271
271
272 :convert.svn.branches: specify the directory containing branches.
272 :convert.svn.branches: specify the directory containing branches.
273 The default is ``branches``.
273 The default is ``branches``.
274
274
275 :convert.svn.tags: specify the directory containing tags. The
275 :convert.svn.tags: specify the directory containing tags. The
276 default is ``tags``.
276 default is ``tags``.
277
277
278 :convert.svn.trunk: specify the name of the trunk branch. The
278 :convert.svn.trunk: specify the name of the trunk branch. The
279 default is ``trunk``.
279 default is ``trunk``.
280
280
281 :convert.localtimezone: use local time (as determined by the TZ
281 :convert.localtimezone: use local time (as determined by the TZ
282 environment variable) for changeset date/times. The default
282 environment variable) for changeset date/times. The default
283 is False (use UTC).
283 is False (use UTC).
284
284
285 Source history can be retrieved starting at a specific revision,
285 Source history can be retrieved starting at a specific revision,
286 instead of being integrally converted. Only single branch
286 instead of being integrally converted. Only single branch
287 conversions are supported.
287 conversions are supported.
288
288
289 :convert.svn.startrev: specify start Subversion revision number.
289 :convert.svn.startrev: specify start Subversion revision number.
290 The default is 0.
290 The default is 0.
291
291
292 Git Source
292 Git Source
293 ##########
293 ##########
294
294
295 The Git importer converts commits from all reachable branches (refs
295 The Git importer converts commits from all reachable branches (refs
296 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
296 in refs/heads) and remotes (refs in refs/remotes) to Mercurial.
297 Branches are converted to bookmarks with the same name, with the
297 Branches are converted to bookmarks with the same name, with the
298 leading 'refs/heads' stripped. Git submodules are converted to Git
298 leading 'refs/heads' stripped. Git submodules are converted to Git
299 subrepos in Mercurial.
299 subrepos in Mercurial.
300
300
301 The following options can be set with ``--config``:
301 The following options can be set with ``--config``:
302
302
303 :convert.git.similarity: specify how similar files modified in a
303 :convert.git.similarity: specify how similar files modified in a
304 commit must be to be imported as renames or copies, as a
304 commit must be to be imported as renames or copies, as a
305 percentage between ``0`` (disabled) and ``100`` (files must be
305 percentage between ``0`` (disabled) and ``100`` (files must be
306 identical). For example, ``90`` means that a delete/add pair will
306 identical). For example, ``90`` means that a delete/add pair will
307 be imported as a rename if more than 90% of the file hasn't
307 be imported as a rename if more than 90% of the file hasn't
308 changed. The default is ``50``.
308 changed. The default is ``50``.
309
309
310 :convert.git.findcopiesharder: while detecting copies, look at all
310 :convert.git.findcopiesharder: while detecting copies, look at all
311 files in the working copy instead of just changed ones. This
311 files in the working copy instead of just changed ones. This
312 is very expensive for large projects, and is only effective when
312 is very expensive for large projects, and is only effective when
313 ``convert.git.similarity`` is greater than 0. The default is False.
313 ``convert.git.similarity`` is greater than 0. The default is False.
314
314
315 :convert.git.remoteprefix: remote refs are converted as bookmarks with
315 :convert.git.remoteprefix: remote refs are converted as bookmarks with
316 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
316 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
317 is 'remote'.
317 is 'remote'.
318
318
319 :convert.git.skipsubmodules: does not convert root level .gitmodules files
320 or files with 160000 mode indicating a submodule. Default is False.
321
319 Perforce Source
322 Perforce Source
320 ###############
323 ###############
321
324
322 The Perforce (P4) importer can be given a p4 depot path or a
325 The Perforce (P4) importer can be given a p4 depot path or a
323 client specification as source. It will convert all files in the
326 client specification as source. It will convert all files in the
324 source to a flat Mercurial repository, ignoring labels, branches
327 source to a flat Mercurial repository, ignoring labels, branches
325 and integrations. Note that when a depot path is given you then
328 and integrations. Note that when a depot path is given you then
326 usually should specify a target directory, because otherwise the
329 usually should specify a target directory, because otherwise the
327 target may be named ``...-hg``.
330 target may be named ``...-hg``.
328
331
329 The following options can be set with ``--config``:
332 The following options can be set with ``--config``:
330
333
331 :convert.p4.encoding: specify the encoding to use when decoding standard
334 :convert.p4.encoding: specify the encoding to use when decoding standard
332 output of the Perforce command line tool. The default is default system
335 output of the Perforce command line tool. The default is default system
333 encoding.
336 encoding.
334
337
335 :convert.p4.startrev: specify initial Perforce revision (a
338 :convert.p4.startrev: specify initial Perforce revision (a
336 Perforce changelist number).
339 Perforce changelist number).
337
340
338 Mercurial Destination
341 Mercurial Destination
339 #####################
342 #####################
340
343
341 The Mercurial destination will recognize Mercurial subrepositories in the
344 The Mercurial destination will recognize Mercurial subrepositories in the
342 destination directory, and update the .hgsubstate file automatically if the
345 destination directory, and update the .hgsubstate file automatically if the
343 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
346 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
344 Converting a repository with subrepositories requires converting a single
347 Converting a repository with subrepositories requires converting a single
345 repository at a time, from the bottom up.
348 repository at a time, from the bottom up.
346
349
347 .. container:: verbose
350 .. container:: verbose
348
351
349 An example showing how to convert a repository with subrepositories::
352 An example showing how to convert a repository with subrepositories::
350
353
351 # so convert knows the type when it sees a non empty destination
354 # so convert knows the type when it sees a non empty destination
352 $ hg init converted
355 $ hg init converted
353
356
354 $ hg convert orig/sub1 converted/sub1
357 $ hg convert orig/sub1 converted/sub1
355 $ hg convert orig/sub2 converted/sub2
358 $ hg convert orig/sub2 converted/sub2
356 $ hg convert orig converted
359 $ hg convert orig converted
357
360
358 The following options are supported:
361 The following options are supported:
359
362
360 :convert.hg.clonebranches: dispatch source branches in separate
363 :convert.hg.clonebranches: dispatch source branches in separate
361 clones. The default is False.
364 clones. The default is False.
362
365
363 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
366 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
364 ``default``.
367 ``default``.
365
368
366 :convert.hg.usebranchnames: preserve branch names. The default is
369 :convert.hg.usebranchnames: preserve branch names. The default is
367 True.
370 True.
368
371
369 :convert.hg.sourcename: records the given string as a 'convert_source' extra
372 :convert.hg.sourcename: records the given string as a 'convert_source' extra
370 value on each commit made in the target repository. The default is None.
373 value on each commit made in the target repository. The default is None.
371
374
372 All Destinations
375 All Destinations
373 ################
376 ################
374
377
375 All destination types accept the following options:
378 All destination types accept the following options:
376
379
377 :convert.skiptags: does not convert tags from the source repo to the target
380 :convert.skiptags: does not convert tags from the source repo to the target
378 repo. The default is False.
381 repo. The default is False.
379 """
382 """
380 return convcmd.convert(ui, src, dest, revmapfile, **opts)
383 return convcmd.convert(ui, src, dest, revmapfile, **opts)
381
384
382 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
385 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
383 def debugsvnlog(ui, **opts):
386 def debugsvnlog(ui, **opts):
384 return subversion.debugsvnlog(ui, **opts)
387 return subversion.debugsvnlog(ui, **opts)
385
388
386 @command('debugcvsps',
389 @command('debugcvsps',
387 [
390 [
388 # Main options shared with cvsps-2.1
391 # Main options shared with cvsps-2.1
389 ('b', 'branches', [], _('only return changes on specified branches')),
392 ('b', 'branches', [], _('only return changes on specified branches')),
390 ('p', 'prefix', '', _('prefix to remove from file names')),
393 ('p', 'prefix', '', _('prefix to remove from file names')),
391 ('r', 'revisions', [],
394 ('r', 'revisions', [],
392 _('only return changes after or between specified tags')),
395 _('only return changes after or between specified tags')),
393 ('u', 'update-cache', None, _("update cvs log cache")),
396 ('u', 'update-cache', None, _("update cvs log cache")),
394 ('x', 'new-cache', None, _("create new cvs log cache")),
397 ('x', 'new-cache', None, _("create new cvs log cache")),
395 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
398 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
396 ('', 'root', '', _('specify cvsroot')),
399 ('', 'root', '', _('specify cvsroot')),
397 # Options specific to builtin cvsps
400 # Options specific to builtin cvsps
398 ('', 'parents', '', _('show parent changesets')),
401 ('', 'parents', '', _('show parent changesets')),
399 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
402 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
400 # Options that are ignored for compatibility with cvsps-2.1
403 # Options that are ignored for compatibility with cvsps-2.1
401 ('A', 'cvs-direct', None, _('ignored for compatibility')),
404 ('A', 'cvs-direct', None, _('ignored for compatibility')),
402 ],
405 ],
403 _('hg debugcvsps [OPTION]... [PATH]...'),
406 _('hg debugcvsps [OPTION]... [PATH]...'),
404 norepo=True)
407 norepo=True)
405 def debugcvsps(ui, *args, **opts):
408 def debugcvsps(ui, *args, **opts):
406 '''create changeset information from CVS
409 '''create changeset information from CVS
407
410
408 This command is intended as a debugging tool for the CVS to
411 This command is intended as a debugging tool for the CVS to
409 Mercurial converter, and can be used as a direct replacement for
412 Mercurial converter, and can be used as a direct replacement for
410 cvsps.
413 cvsps.
411
414
412 Hg debugcvsps reads the CVS rlog for current directory (or any
415 Hg debugcvsps reads the CVS rlog for current directory (or any
413 named directory) in the CVS repository, and converts the log to a
416 named directory) in the CVS repository, and converts the log to a
414 series of changesets based on matching commit log entries and
417 series of changesets based on matching commit log entries and
415 dates.'''
418 dates.'''
416 return cvsps.debugcvsps(ui, *args, **opts)
419 return cvsps.debugcvsps(ui, *args, **opts)
417
420
418 def kwconverted(ctx, name):
421 def kwconverted(ctx, name):
419 rev = ctx.extra().get('convert_revision', '')
422 rev = ctx.extra().get('convert_revision', '')
420 if rev.startswith('svn:'):
423 if rev.startswith('svn:'):
421 if name == 'svnrev':
424 if name == 'svnrev':
422 return str(subversion.revsplit(rev)[2])
425 return str(subversion.revsplit(rev)[2])
423 elif name == 'svnpath':
426 elif name == 'svnpath':
424 return subversion.revsplit(rev)[1]
427 return subversion.revsplit(rev)[1]
425 elif name == 'svnuuid':
428 elif name == 'svnuuid':
426 return subversion.revsplit(rev)[0]
429 return subversion.revsplit(rev)[0]
427 return rev
430 return rev
428
431
429 def kwsvnrev(repo, ctx, **args):
432 def kwsvnrev(repo, ctx, **args):
430 """:svnrev: String. Converted subversion revision number."""
433 """:svnrev: String. Converted subversion revision number."""
431 return kwconverted(ctx, 'svnrev')
434 return kwconverted(ctx, 'svnrev')
432
435
433 def kwsvnpath(repo, ctx, **args):
436 def kwsvnpath(repo, ctx, **args):
434 """:svnpath: String. Converted subversion revision project path."""
437 """:svnpath: String. Converted subversion revision project path."""
435 return kwconverted(ctx, 'svnpath')
438 return kwconverted(ctx, 'svnpath')
436
439
437 def kwsvnuuid(repo, ctx, **args):
440 def kwsvnuuid(repo, ctx, **args):
438 """:svnuuid: String. Converted subversion revision repository identifier."""
441 """:svnuuid: String. Converted subversion revision repository identifier."""
439 return kwconverted(ctx, 'svnuuid')
442 return kwconverted(ctx, 'svnuuid')
440
443
441 def extsetup(ui):
444 def extsetup(ui):
442 templatekw.keywords['svnrev'] = kwsvnrev
445 templatekw.keywords['svnrev'] = kwsvnrev
443 templatekw.keywords['svnpath'] = kwsvnpath
446 templatekw.keywords['svnpath'] = kwsvnpath
444 templatekw.keywords['svnuuid'] = kwsvnuuid
447 templatekw.keywords['svnuuid'] = kwsvnuuid
445
448
446 # tell hggettext to extract docstrings from these functions:
449 # tell hggettext to extract docstrings from these functions:
447 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
450 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
@@ -1,410 +1,416 b''
1 # git.py - git support for the convert extension
1 # git.py - git support for the 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 import os
8 import os
9 import subprocess
9 import subprocess
10 from mercurial import util, config, error
10 from mercurial import util, config, error
11 from mercurial.node import hex, nullid
11 from mercurial.node import hex, nullid
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 from common import NoRepo, commit, converter_source, checktool
14 from common import NoRepo, commit, converter_source, checktool
15
15
16 class submodule(object):
16 class submodule(object):
17 def __init__(self, path, node, url):
17 def __init__(self, path, node, url):
18 self.path = path
18 self.path = path
19 self.node = node
19 self.node = node
20 self.url = url
20 self.url = url
21
21
22 def hgsub(self):
22 def hgsub(self):
23 return "%s = [git]%s" % (self.path, self.url)
23 return "%s = [git]%s" % (self.path, self.url)
24
24
25 def hgsubstate(self):
25 def hgsubstate(self):
26 return "%s %s" % (self.node, self.path)
26 return "%s %s" % (self.node, self.path)
27
27
28 class convert_git(converter_source):
28 class convert_git(converter_source):
29 # Windows does not support GIT_DIR= construct while other systems
29 # Windows does not support GIT_DIR= construct while other systems
30 # cannot remove environment variable. Just assume none have
30 # cannot remove environment variable. Just assume none have
31 # both issues.
31 # both issues.
32 if util.safehasattr(os, 'unsetenv'):
32 if util.safehasattr(os, 'unsetenv'):
33 def gitopen(self, s, err=None):
33 def gitopen(self, s, err=None):
34 prevgitdir = os.environ.get('GIT_DIR')
34 prevgitdir = os.environ.get('GIT_DIR')
35 os.environ['GIT_DIR'] = self.path
35 os.environ['GIT_DIR'] = self.path
36 try:
36 try:
37 if err == subprocess.PIPE:
37 if err == subprocess.PIPE:
38 (stdin, stdout, stderr) = util.popen3(s)
38 (stdin, stdout, stderr) = util.popen3(s)
39 return stdout
39 return stdout
40 elif err == subprocess.STDOUT:
40 elif err == subprocess.STDOUT:
41 return self.popen_with_stderr(s)
41 return self.popen_with_stderr(s)
42 else:
42 else:
43 return util.popen(s, 'rb')
43 return util.popen(s, 'rb')
44 finally:
44 finally:
45 if prevgitdir is None:
45 if prevgitdir is None:
46 del os.environ['GIT_DIR']
46 del os.environ['GIT_DIR']
47 else:
47 else:
48 os.environ['GIT_DIR'] = prevgitdir
48 os.environ['GIT_DIR'] = prevgitdir
49
49
50 def gitpipe(self, s):
50 def gitpipe(self, s):
51 prevgitdir = os.environ.get('GIT_DIR')
51 prevgitdir = os.environ.get('GIT_DIR')
52 os.environ['GIT_DIR'] = self.path
52 os.environ['GIT_DIR'] = self.path
53 try:
53 try:
54 return util.popen3(s)
54 return util.popen3(s)
55 finally:
55 finally:
56 if prevgitdir is None:
56 if prevgitdir is None:
57 del os.environ['GIT_DIR']
57 del os.environ['GIT_DIR']
58 else:
58 else:
59 os.environ['GIT_DIR'] = prevgitdir
59 os.environ['GIT_DIR'] = prevgitdir
60
60
61 else:
61 else:
62 def gitopen(self, s, err=None):
62 def gitopen(self, s, err=None):
63 if err == subprocess.PIPE:
63 if err == subprocess.PIPE:
64 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
64 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
65 return so
65 return so
66 elif err == subprocess.STDOUT:
66 elif err == subprocess.STDOUT:
67 return self.popen_with_stderr(s)
67 return self.popen_with_stderr(s)
68 else:
68 else:
69 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
69 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
70
70
71 def gitpipe(self, s):
71 def gitpipe(self, s):
72 return util.popen3('GIT_DIR=%s %s' % (self.path, s))
72 return util.popen3('GIT_DIR=%s %s' % (self.path, s))
73
73
74 def popen_with_stderr(self, s):
74 def popen_with_stderr(self, s):
75 p = subprocess.Popen(s, shell=True, bufsize=-1,
75 p = subprocess.Popen(s, shell=True, bufsize=-1,
76 close_fds=util.closefds,
76 close_fds=util.closefds,
77 stdin=subprocess.PIPE,
77 stdin=subprocess.PIPE,
78 stdout=subprocess.PIPE,
78 stdout=subprocess.PIPE,
79 stderr=subprocess.STDOUT,
79 stderr=subprocess.STDOUT,
80 universal_newlines=False,
80 universal_newlines=False,
81 env=None)
81 env=None)
82 return p.stdout
82 return p.stdout
83
83
84 def gitread(self, s):
84 def gitread(self, s):
85 fh = self.gitopen(s)
85 fh = self.gitopen(s)
86 data = fh.read()
86 data = fh.read()
87 return data, fh.close()
87 return data, fh.close()
88
88
89 def __init__(self, ui, path, revs=None):
89 def __init__(self, ui, path, revs=None):
90 super(convert_git, self).__init__(ui, path, revs=revs)
90 super(convert_git, self).__init__(ui, path, revs=revs)
91
91
92 if os.path.isdir(path + "/.git"):
92 if os.path.isdir(path + "/.git"):
93 path += "/.git"
93 path += "/.git"
94 if not os.path.exists(path + "/objects"):
94 if not os.path.exists(path + "/objects"):
95 raise NoRepo(_("%s does not look like a Git repository") % path)
95 raise NoRepo(_("%s does not look like a Git repository") % path)
96
96
97 # The default value (50) is based on the default for 'git diff'.
97 # The default value (50) is based on the default for 'git diff'.
98 similarity = ui.configint('convert', 'git.similarity', default=50)
98 similarity = ui.configint('convert', 'git.similarity', default=50)
99 if similarity < 0 or similarity > 100:
99 if similarity < 0 or similarity > 100:
100 raise util.Abort(_('similarity must be between 0 and 100'))
100 raise util.Abort(_('similarity must be between 0 and 100'))
101 if similarity > 0:
101 if similarity > 0:
102 self.simopt = '-C%d%%' % similarity
102 self.simopt = '-C%d%%' % similarity
103 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
103 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
104 False)
104 False)
105 if findcopiesharder:
105 if findcopiesharder:
106 self.simopt += ' --find-copies-harder'
106 self.simopt += ' --find-copies-harder'
107 else:
107 else:
108 self.simopt = ''
108 self.simopt = ''
109
109
110 checktool('git', 'git')
110 checktool('git', 'git')
111
111
112 self.path = path
112 self.path = path
113 self.submodules = []
113 self.submodules = []
114
114
115 self.catfilepipe = self.gitpipe('git cat-file --batch')
115 self.catfilepipe = self.gitpipe('git cat-file --batch')
116
116
117 def after(self):
117 def after(self):
118 for f in self.catfilepipe:
118 for f in self.catfilepipe:
119 f.close()
119 f.close()
120
120
121 def getheads(self):
121 def getheads(self):
122 if not self.revs:
122 if not self.revs:
123 heads, ret = self.gitread('git rev-parse --branches --remotes')
123 heads, ret = self.gitread('git rev-parse --branches --remotes')
124 heads = heads.splitlines()
124 heads = heads.splitlines()
125 if ret:
125 if ret:
126 raise util.Abort(_('cannot retrieve git heads'))
126 raise util.Abort(_('cannot retrieve git heads'))
127 else:
127 else:
128 heads = []
128 heads = []
129 for rev in self.revs:
129 for rev in self.revs:
130 rawhead, ret = self.gitread("git rev-parse --verify %s" % rev)
130 rawhead, ret = self.gitread("git rev-parse --verify %s" % rev)
131 heads.append(rawhead[:-1])
131 heads.append(rawhead[:-1])
132 if ret:
132 if ret:
133 raise util.Abort(_('cannot retrieve git head "%s"') % rev)
133 raise util.Abort(_('cannot retrieve git head "%s"') % rev)
134 return heads
134 return heads
135
135
136 def catfile(self, rev, type):
136 def catfile(self, rev, type):
137 if rev == hex(nullid):
137 if rev == hex(nullid):
138 raise IOError
138 raise IOError
139 self.catfilepipe[0].write(rev+'\n')
139 self.catfilepipe[0].write(rev+'\n')
140 self.catfilepipe[0].flush()
140 self.catfilepipe[0].flush()
141 info = self.catfilepipe[1].readline().split()
141 info = self.catfilepipe[1].readline().split()
142 if info[1] != type:
142 if info[1] != type:
143 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
143 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
144 size = int(info[2])
144 size = int(info[2])
145 data = self.catfilepipe[1].read(size)
145 data = self.catfilepipe[1].read(size)
146 if len(data) < size:
146 if len(data) < size:
147 raise util.Abort(_('cannot read %r object at %s: unexpected size')
147 raise util.Abort(_('cannot read %r object at %s: unexpected size')
148 % (type, rev))
148 % (type, rev))
149 # read the trailing newline
149 # read the trailing newline
150 self.catfilepipe[1].read(1)
150 self.catfilepipe[1].read(1)
151 return data
151 return data
152
152
153 def getfile(self, name, rev):
153 def getfile(self, name, rev):
154 if rev == hex(nullid):
154 if rev == hex(nullid):
155 return None, None
155 return None, None
156 if name == '.hgsub':
156 if name == '.hgsub':
157 data = '\n'.join([m.hgsub() for m in self.submoditer()])
157 data = '\n'.join([m.hgsub() for m in self.submoditer()])
158 mode = ''
158 mode = ''
159 elif name == '.hgsubstate':
159 elif name == '.hgsubstate':
160 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
160 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
161 mode = ''
161 mode = ''
162 else:
162 else:
163 data = self.catfile(rev, "blob")
163 data = self.catfile(rev, "blob")
164 mode = self.modecache[(name, rev)]
164 mode = self.modecache[(name, rev)]
165 return data, mode
165 return data, mode
166
166
167 def submoditer(self):
167 def submoditer(self):
168 null = hex(nullid)
168 null = hex(nullid)
169 for m in sorted(self.submodules, key=lambda p: p.path):
169 for m in sorted(self.submodules, key=lambda p: p.path):
170 if m.node != null:
170 if m.node != null:
171 yield m
171 yield m
172
172
173 def parsegitmodules(self, content):
173 def parsegitmodules(self, content):
174 """Parse the formatted .gitmodules file, example file format:
174 """Parse the formatted .gitmodules file, example file format:
175 [submodule "sub"]\n
175 [submodule "sub"]\n
176 \tpath = sub\n
176 \tpath = sub\n
177 \turl = git://giturl\n
177 \turl = git://giturl\n
178 """
178 """
179 self.submodules = []
179 self.submodules = []
180 c = config.config()
180 c = config.config()
181 # Each item in .gitmodules starts with whitespace that cant be parsed
181 # Each item in .gitmodules starts with whitespace that cant be parsed
182 c.parse('.gitmodules', '\n'.join(line.strip() for line in
182 c.parse('.gitmodules', '\n'.join(line.strip() for line in
183 content.split('\n')))
183 content.split('\n')))
184 for sec in c.sections():
184 for sec in c.sections():
185 s = c[sec]
185 s = c[sec]
186 if 'url' in s and 'path' in s:
186 if 'url' in s and 'path' in s:
187 self.submodules.append(submodule(s['path'], '', s['url']))
187 self.submodules.append(submodule(s['path'], '', s['url']))
188
188
189 def retrievegitmodules(self, version):
189 def retrievegitmodules(self, version):
190 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
190 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
191 if ret:
191 if ret:
192 # This can happen if a file is in the repo that has permissions
192 # This can happen if a file is in the repo that has permissions
193 # 160000, but there is no .gitmodules file.
193 # 160000, but there is no .gitmodules file.
194 self.ui.warn(_("warning: cannot read submodules config file in "
194 self.ui.warn(_("warning: cannot read submodules config file in "
195 "%s\n") % version)
195 "%s\n") % version)
196 return
196 return
197
197
198 try:
198 try:
199 self.parsegitmodules(modules)
199 self.parsegitmodules(modules)
200 except error.ParseError:
200 except error.ParseError:
201 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
201 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
202 % version)
202 % version)
203 return
203 return
204
204
205 for m in self.submodules:
205 for m in self.submodules:
206 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
206 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
207 if ret:
207 if ret:
208 continue
208 continue
209 m.node = node.strip()
209 m.node = node.strip()
210
210
211 def getchanges(self, version, full):
211 def getchanges(self, version, full):
212 if full:
212 if full:
213 raise util.Abort(_("convert from git do not support --full"))
213 raise util.Abort(_("convert from git do not support --full"))
214 self.modecache = {}
214 self.modecache = {}
215 fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % (
215 fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % (
216 self.simopt, version))
216 self.simopt, version))
217 changes = []
217 changes = []
218 copies = {}
218 copies = {}
219 seen = set()
219 seen = set()
220 entry = None
220 entry = None
221 subexists = [False]
221 subexists = [False]
222 subdeleted = [False]
222 subdeleted = [False]
223 difftree = fh.read().split('\x00')
223 difftree = fh.read().split('\x00')
224 lcount = len(difftree)
224 lcount = len(difftree)
225 i = 0
225 i = 0
226
226
227 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules',
228 False)
227 def add(entry, f, isdest):
229 def add(entry, f, isdest):
228 seen.add(f)
230 seen.add(f)
229 h = entry[3]
231 h = entry[3]
230 p = (entry[1] == "100755")
232 p = (entry[1] == "100755")
231 s = (entry[1] == "120000")
233 s = (entry[1] == "120000")
232 renamesource = (not isdest and entry[4][0] == 'R')
234 renamesource = (not isdest and entry[4][0] == 'R')
233
235
234 if f == '.gitmodules':
236 if f == '.gitmodules':
237 if skipsubmodules:
238 return
239
235 subexists[0] = True
240 subexists[0] = True
236 if entry[4] == 'D' or renamesource:
241 if entry[4] == 'D' or renamesource:
237 subdeleted[0] = True
242 subdeleted[0] = True
238 changes.append(('.hgsub', hex(nullid)))
243 changes.append(('.hgsub', hex(nullid)))
239 else:
244 else:
240 changes.append(('.hgsub', ''))
245 changes.append(('.hgsub', ''))
241 elif entry[1] == '160000' or entry[0] == ':160000':
246 elif entry[1] == '160000' or entry[0] == ':160000':
247 if not skipsubmodules:
242 subexists[0] = True
248 subexists[0] = True
243 else:
249 else:
244 if renamesource:
250 if renamesource:
245 h = hex(nullid)
251 h = hex(nullid)
246 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
252 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
247 changes.append((f, h))
253 changes.append((f, h))
248
254
249 while i < lcount:
255 while i < lcount:
250 l = difftree[i]
256 l = difftree[i]
251 i += 1
257 i += 1
252 if not entry:
258 if not entry:
253 if not l.startswith(':'):
259 if not l.startswith(':'):
254 continue
260 continue
255 entry = l.split()
261 entry = l.split()
256 continue
262 continue
257 f = l
263 f = l
258 if entry[4][0] == 'C':
264 if entry[4][0] == 'C':
259 copysrc = f
265 copysrc = f
260 copydest = difftree[i]
266 copydest = difftree[i]
261 i += 1
267 i += 1
262 f = copydest
268 f = copydest
263 copies[copydest] = copysrc
269 copies[copydest] = copysrc
264 if f not in seen:
270 if f not in seen:
265 add(entry, f, False)
271 add(entry, f, False)
266 # A file can be copied multiple times, or modified and copied
272 # A file can be copied multiple times, or modified and copied
267 # simultaneously. So f can be repeated even if fdest isn't.
273 # simultaneously. So f can be repeated even if fdest isn't.
268 if entry[4][0] == 'R':
274 if entry[4][0] == 'R':
269 # rename: next line is the destination
275 # rename: next line is the destination
270 fdest = difftree[i]
276 fdest = difftree[i]
271 i += 1
277 i += 1
272 if fdest not in seen:
278 if fdest not in seen:
273 add(entry, fdest, True)
279 add(entry, fdest, True)
274 # .gitmodules isn't imported at all, so it being copied to
280 # .gitmodules isn't imported at all, so it being copied to
275 # and fro doesn't really make sense
281 # and fro doesn't really make sense
276 if f != '.gitmodules' and fdest != '.gitmodules':
282 if f != '.gitmodules' and fdest != '.gitmodules':
277 copies[fdest] = f
283 copies[fdest] = f
278 entry = None
284 entry = None
279 if fh.close():
285 if fh.close():
280 raise util.Abort(_('cannot read changes in %s') % version)
286 raise util.Abort(_('cannot read changes in %s') % version)
281
287
282 if subexists[0]:
288 if subexists[0]:
283 if subdeleted[0]:
289 if subdeleted[0]:
284 changes.append(('.hgsubstate', hex(nullid)))
290 changes.append(('.hgsubstate', hex(nullid)))
285 else:
291 else:
286 self.retrievegitmodules(version)
292 self.retrievegitmodules(version)
287 changes.append(('.hgsubstate', ''))
293 changes.append(('.hgsubstate', ''))
288 return (changes, copies, set())
294 return (changes, copies, set())
289
295
290 def getcommit(self, version):
296 def getcommit(self, version):
291 c = self.catfile(version, "commit") # read the commit hash
297 c = self.catfile(version, "commit") # read the commit hash
292 end = c.find("\n\n")
298 end = c.find("\n\n")
293 message = c[end + 2:]
299 message = c[end + 2:]
294 message = self.recode(message)
300 message = self.recode(message)
295 l = c[:end].splitlines()
301 l = c[:end].splitlines()
296 parents = []
302 parents = []
297 author = committer = None
303 author = committer = None
298 for e in l[1:]:
304 for e in l[1:]:
299 n, v = e.split(" ", 1)
305 n, v = e.split(" ", 1)
300 if n == "author":
306 if n == "author":
301 p = v.split()
307 p = v.split()
302 tm, tz = p[-2:]
308 tm, tz = p[-2:]
303 author = " ".join(p[:-2])
309 author = " ".join(p[:-2])
304 if author[0] == "<": author = author[1:-1]
310 if author[0] == "<": author = author[1:-1]
305 author = self.recode(author)
311 author = self.recode(author)
306 if n == "committer":
312 if n == "committer":
307 p = v.split()
313 p = v.split()
308 tm, tz = p[-2:]
314 tm, tz = p[-2:]
309 committer = " ".join(p[:-2])
315 committer = " ".join(p[:-2])
310 if committer[0] == "<": committer = committer[1:-1]
316 if committer[0] == "<": committer = committer[1:-1]
311 committer = self.recode(committer)
317 committer = self.recode(committer)
312 if n == "parent":
318 if n == "parent":
313 parents.append(v)
319 parents.append(v)
314
320
315 if committer and committer != author:
321 if committer and committer != author:
316 message += "\ncommitter: %s\n" % committer
322 message += "\ncommitter: %s\n" % committer
317 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
323 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
318 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
324 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
319 date = tm + " " + str(tz)
325 date = tm + " " + str(tz)
320
326
321 c = commit(parents=parents, date=date, author=author, desc=message,
327 c = commit(parents=parents, date=date, author=author, desc=message,
322 rev=version)
328 rev=version)
323 return c
329 return c
324
330
325 def numcommits(self):
331 def numcommits(self):
326 return len([None for _ in self.gitopen('git rev-list --all')])
332 return len([None for _ in self.gitopen('git rev-list --all')])
327
333
328 def gettags(self):
334 def gettags(self):
329 tags = {}
335 tags = {}
330 alltags = {}
336 alltags = {}
331 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
337 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
332 err=subprocess.STDOUT)
338 err=subprocess.STDOUT)
333 prefix = 'refs/tags/'
339 prefix = 'refs/tags/'
334
340
335 # Build complete list of tags, both annotated and bare ones
341 # Build complete list of tags, both annotated and bare ones
336 for line in fh:
342 for line in fh:
337 line = line.strip()
343 line = line.strip()
338 if line.startswith("error:") or line.startswith("fatal:"):
344 if line.startswith("error:") or line.startswith("fatal:"):
339 raise util.Abort(_('cannot read tags from %s') % self.path)
345 raise util.Abort(_('cannot read tags from %s') % self.path)
340 node, tag = line.split(None, 1)
346 node, tag = line.split(None, 1)
341 if not tag.startswith(prefix):
347 if not tag.startswith(prefix):
342 continue
348 continue
343 alltags[tag[len(prefix):]] = node
349 alltags[tag[len(prefix):]] = node
344 if fh.close():
350 if fh.close():
345 raise util.Abort(_('cannot read tags from %s') % self.path)
351 raise util.Abort(_('cannot read tags from %s') % self.path)
346
352
347 # Filter out tag objects for annotated tag refs
353 # Filter out tag objects for annotated tag refs
348 for tag in alltags:
354 for tag in alltags:
349 if tag.endswith('^{}'):
355 if tag.endswith('^{}'):
350 tags[tag[:-3]] = alltags[tag]
356 tags[tag[:-3]] = alltags[tag]
351 else:
357 else:
352 if tag + '^{}' in alltags:
358 if tag + '^{}' in alltags:
353 continue
359 continue
354 else:
360 else:
355 tags[tag] = alltags[tag]
361 tags[tag] = alltags[tag]
356
362
357 return tags
363 return tags
358
364
359 def getchangedfiles(self, version, i):
365 def getchangedfiles(self, version, i):
360 changes = []
366 changes = []
361 if i is None:
367 if i is None:
362 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
368 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
363 for l in fh:
369 for l in fh:
364 if "\t" not in l:
370 if "\t" not in l:
365 continue
371 continue
366 m, f = l[:-1].split("\t")
372 m, f = l[:-1].split("\t")
367 changes.append(f)
373 changes.append(f)
368 else:
374 else:
369 fh = self.gitopen('git diff-tree --name-only --root -r %s '
375 fh = self.gitopen('git diff-tree --name-only --root -r %s '
370 '"%s^%s" --' % (version, version, i + 1))
376 '"%s^%s" --' % (version, version, i + 1))
371 changes = [f.rstrip('\n') for f in fh]
377 changes = [f.rstrip('\n') for f in fh]
372 if fh.close():
378 if fh.close():
373 raise util.Abort(_('cannot read changes in %s') % version)
379 raise util.Abort(_('cannot read changes in %s') % version)
374
380
375 return changes
381 return changes
376
382
377 def getbookmarks(self):
383 def getbookmarks(self):
378 bookmarks = {}
384 bookmarks = {}
379
385
380 # Handle local and remote branches
386 # Handle local and remote branches
381 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
387 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
382 reftypes = [
388 reftypes = [
383 # (git prefix, hg prefix)
389 # (git prefix, hg prefix)
384 ('refs/remotes/origin/', remoteprefix + '/'),
390 ('refs/remotes/origin/', remoteprefix + '/'),
385 ('refs/heads/', '')
391 ('refs/heads/', '')
386 ]
392 ]
387
393
388 exclude = set([
394 exclude = set([
389 'refs/remotes/origin/HEAD',
395 'refs/remotes/origin/HEAD',
390 ])
396 ])
391
397
392 try:
398 try:
393 fh = self.gitopen('git show-ref', err=subprocess.PIPE)
399 fh = self.gitopen('git show-ref', err=subprocess.PIPE)
394 for line in fh:
400 for line in fh:
395 line = line.strip()
401 line = line.strip()
396 rev, name = line.split(None, 1)
402 rev, name = line.split(None, 1)
397 # Process each type of branch
403 # Process each type of branch
398 for gitprefix, hgprefix in reftypes:
404 for gitprefix, hgprefix in reftypes:
399 if not name.startswith(gitprefix) or name in exclude:
405 if not name.startswith(gitprefix) or name in exclude:
400 continue
406 continue
401 name = '%s%s' % (hgprefix, name[len(gitprefix):])
407 name = '%s%s' % (hgprefix, name[len(gitprefix):])
402 bookmarks[name] = rev
408 bookmarks[name] = rev
403 except Exception:
409 except Exception:
404 pass
410 pass
405
411
406 return bookmarks
412 return bookmarks
407
413
408 def checkrevformat(self, revstr, mapname='splicemap'):
414 def checkrevformat(self, revstr, mapname='splicemap'):
409 """ git revision string is a 40 byte hex """
415 """ git revision string is a 40 byte hex """
410 self.checkhexformat(revstr, mapname)
416 self.checkhexformat(revstr, mapname)
@@ -1,728 +1,734 b''
1 #require git
1 #require git
2
2
3 $ echo "[core]" >> $HOME/.gitconfig
3 $ echo "[core]" >> $HOME/.gitconfig
4 $ echo "autocrlf = false" >> $HOME/.gitconfig
4 $ echo "autocrlf = false" >> $HOME/.gitconfig
5 $ echo "[core]" >> $HOME/.gitconfig
5 $ echo "[core]" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
7 $ echo "[extensions]" >> $HGRCPATH
7 $ echo "[extensions]" >> $HGRCPATH
8 $ echo "convert=" >> $HGRCPATH
8 $ echo "convert=" >> $HGRCPATH
9 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
9 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
10 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
10 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
11 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
11 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
12 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
12 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
13 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
13 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
14 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
14 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
15 $ INVALIDID1=afd12345af
15 $ INVALIDID1=afd12345af
16 $ INVALIDID2=28173x36ddd1e67bf7098d541130558ef5534a86
16 $ INVALIDID2=28173x36ddd1e67bf7098d541130558ef5534a86
17 $ VALIDID1=39b3d83f9a69a9ba4ebb111461071a0af0027357
17 $ VALIDID1=39b3d83f9a69a9ba4ebb111461071a0af0027357
18 $ VALIDID2=8dd6476bd09d9c7776355dc454dafe38efaec5da
18 $ VALIDID2=8dd6476bd09d9c7776355dc454dafe38efaec5da
19 $ count=10
19 $ count=10
20 $ commit()
20 $ commit()
21 > {
21 > {
22 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
22 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
23 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
23 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
24 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
24 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
25 > count=`expr $count + 1`
25 > count=`expr $count + 1`
26 > }
26 > }
27 $ mkdir git-repo
27 $ mkdir git-repo
28 $ cd git-repo
28 $ cd git-repo
29 $ git init-db >/dev/null 2>/dev/null
29 $ git init-db >/dev/null 2>/dev/null
30 $ echo a > a
30 $ echo a > a
31 $ mkdir d
31 $ mkdir d
32 $ echo b > d/b
32 $ echo b > d/b
33 $ git add a d
33 $ git add a d
34 $ commit -a -m t1
34 $ commit -a -m t1
35
35
36 Remove the directory, then try to replace it with a file (issue754)
36 Remove the directory, then try to replace it with a file (issue754)
37
37
38 $ git rm -f d/b
38 $ git rm -f d/b
39 rm 'd/b'
39 rm 'd/b'
40 $ commit -m t2
40 $ commit -m t2
41 $ echo d > d
41 $ echo d > d
42 $ git add d
42 $ git add d
43 $ commit -m t3
43 $ commit -m t3
44 $ echo b >> a
44 $ echo b >> a
45 $ commit -a -m t4.1
45 $ commit -a -m t4.1
46 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
46 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
47 $ echo c > a
47 $ echo c > a
48 $ echo a >> a
48 $ echo a >> a
49 $ commit -a -m t4.2
49 $ commit -a -m t4.2
50 $ git checkout master >/dev/null 2>/dev/null
50 $ git checkout master >/dev/null 2>/dev/null
51 $ git pull --no-commit . other > /dev/null 2>/dev/null
51 $ git pull --no-commit . other > /dev/null 2>/dev/null
52 $ commit -m 'Merge branch other'
52 $ commit -m 'Merge branch other'
53 $ cd ..
53 $ cd ..
54 $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
54 $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
55 > --config progress.delay=0 --config progress.changedelay=0 \
55 > --config progress.delay=0 --config progress.changedelay=0 \
56 > --config progress.refresh=0 --config progress.width=60 \
56 > --config progress.refresh=0 --config progress.width=60 \
57 > --datesort git-repo
57 > --datesort git-repo
58 \r (no-eol) (esc)
58 \r (no-eol) (esc)
59 scanning [======> ] 1/6\r (no-eol) (esc)
59 scanning [======> ] 1/6\r (no-eol) (esc)
60 scanning [=============> ] 2/6\r (no-eol) (esc)
60 scanning [=============> ] 2/6\r (no-eol) (esc)
61 scanning [=====================> ] 3/6\r (no-eol) (esc)
61 scanning [=====================> ] 3/6\r (no-eol) (esc)
62 scanning [============================> ] 4/6\r (no-eol) (esc)
62 scanning [============================> ] 4/6\r (no-eol) (esc)
63 scanning [===================================> ] 5/6\r (no-eol) (esc)
63 scanning [===================================> ] 5/6\r (no-eol) (esc)
64 scanning [===========================================>] 6/6\r (no-eol) (esc)
64 scanning [===========================================>] 6/6\r (no-eol) (esc)
65 \r (no-eol) (esc)
65 \r (no-eol) (esc)
66 \r (no-eol) (esc)
66 \r (no-eol) (esc)
67 converting [ ] 0/6\r (no-eol) (esc)
67 converting [ ] 0/6\r (no-eol) (esc)
68 getting files [==================> ] 1/2\r (no-eol) (esc)
68 getting files [==================> ] 1/2\r (no-eol) (esc)
69 getting files [======================================>] 2/2\r (no-eol) (esc)
69 getting files [======================================>] 2/2\r (no-eol) (esc)
70 \r (no-eol) (esc)
70 \r (no-eol) (esc)
71 \r (no-eol) (esc)
71 \r (no-eol) (esc)
72 converting [======> ] 1/6\r (no-eol) (esc)
72 converting [======> ] 1/6\r (no-eol) (esc)
73 getting files [======================================>] 1/1\r (no-eol) (esc)
73 getting files [======================================>] 1/1\r (no-eol) (esc)
74 \r (no-eol) (esc)
74 \r (no-eol) (esc)
75 \r (no-eol) (esc)
75 \r (no-eol) (esc)
76 converting [=============> ] 2/6\r (no-eol) (esc)
76 converting [=============> ] 2/6\r (no-eol) (esc)
77 getting files [======================================>] 1/1\r (no-eol) (esc)
77 getting files [======================================>] 1/1\r (no-eol) (esc)
78 \r (no-eol) (esc)
78 \r (no-eol) (esc)
79 \r (no-eol) (esc)
79 \r (no-eol) (esc)
80 converting [====================> ] 3/6\r (no-eol) (esc)
80 converting [====================> ] 3/6\r (no-eol) (esc)
81 getting files [======================================>] 1/1\r (no-eol) (esc)
81 getting files [======================================>] 1/1\r (no-eol) (esc)
82 \r (no-eol) (esc)
82 \r (no-eol) (esc)
83 \r (no-eol) (esc)
83 \r (no-eol) (esc)
84 converting [===========================> ] 4/6\r (no-eol) (esc)
84 converting [===========================> ] 4/6\r (no-eol) (esc)
85 getting files [======================================>] 1/1\r (no-eol) (esc)
85 getting files [======================================>] 1/1\r (no-eol) (esc)
86 \r (no-eol) (esc)
86 \r (no-eol) (esc)
87 \r (no-eol) (esc)
87 \r (no-eol) (esc)
88 converting [==================================> ] 5/6\r (no-eol) (esc)
88 converting [==================================> ] 5/6\r (no-eol) (esc)
89 getting files [======================================>] 1/1\r (no-eol) (esc)
89 getting files [======================================>] 1/1\r (no-eol) (esc)
90 \r (no-eol) (esc)
90 \r (no-eol) (esc)
91 assuming destination git-repo-hg
91 assuming destination git-repo-hg
92 initializing destination git-repo-hg repository
92 initializing destination git-repo-hg repository
93 scanning source...
93 scanning source...
94 sorting...
94 sorting...
95 converting...
95 converting...
96 5 t1
96 5 t1
97 4 t2
97 4 t2
98 3 t3
98 3 t3
99 2 t4.1
99 2 t4.1
100 1 t4.2
100 1 t4.2
101 0 Merge branch other
101 0 Merge branch other
102 updating bookmarks
102 updating bookmarks
103 $ hg up -q -R git-repo-hg
103 $ hg up -q -R git-repo-hg
104 $ hg -R git-repo-hg tip -v
104 $ hg -R git-repo-hg tip -v
105 changeset: 5:c78094926be2
105 changeset: 5:c78094926be2
106 bookmark: master
106 bookmark: master
107 tag: tip
107 tag: tip
108 parent: 3:f5f5cb45432b
108 parent: 3:f5f5cb45432b
109 parent: 4:4e174f80c67c
109 parent: 4:4e174f80c67c
110 user: test <test@example.org>
110 user: test <test@example.org>
111 date: Mon Jan 01 00:00:15 2007 +0000
111 date: Mon Jan 01 00:00:15 2007 +0000
112 files: a
112 files: a
113 description:
113 description:
114 Merge branch other
114 Merge branch other
115
115
116
116
117 $ count=10
117 $ count=10
118 $ mkdir git-repo2
118 $ mkdir git-repo2
119 $ cd git-repo2
119 $ cd git-repo2
120 $ git init-db >/dev/null 2>/dev/null
120 $ git init-db >/dev/null 2>/dev/null
121 $ echo foo > foo
121 $ echo foo > foo
122 $ git add foo
122 $ git add foo
123 $ commit -a -m 'add foo'
123 $ commit -a -m 'add foo'
124 $ echo >> foo
124 $ echo >> foo
125 $ commit -a -m 'change foo'
125 $ commit -a -m 'change foo'
126 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
126 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
127 $ echo quux >> quux
127 $ echo quux >> quux
128 $ git add quux
128 $ git add quux
129 $ commit -a -m 'add quux'
129 $ commit -a -m 'add quux'
130 $ echo bar > bar
130 $ echo bar > bar
131 $ git add bar
131 $ git add bar
132 $ commit -a -m 'add bar'
132 $ commit -a -m 'add bar'
133 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
133 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
134 $ echo baz > baz
134 $ echo baz > baz
135 $ git add baz
135 $ git add baz
136 $ commit -a -m 'add baz'
136 $ commit -a -m 'add baz'
137 $ git checkout master >/dev/null 2>/dev/null
137 $ git checkout master >/dev/null 2>/dev/null
138 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
138 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
139 $ commit -m 'Octopus merge'
139 $ commit -m 'Octopus merge'
140 $ echo bar >> bar
140 $ echo bar >> bar
141 $ commit -a -m 'change bar'
141 $ commit -a -m 'change bar'
142 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
142 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
143 $ echo >> foo
143 $ echo >> foo
144 $ commit -a -m 'change foo'
144 $ commit -a -m 'change foo'
145 $ git checkout master >/dev/null 2>/dev/null
145 $ git checkout master >/dev/null 2>/dev/null
146 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
146 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
147 $ commit -m 'Discard change to foo'
147 $ commit -m 'Discard change to foo'
148 $ cd ..
148 $ cd ..
149 $ glog()
149 $ glog()
150 > {
150 > {
151 > hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
151 > hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
152 > }
152 > }
153 $ splitrepo()
153 $ splitrepo()
154 > {
154 > {
155 > msg="$1"
155 > msg="$1"
156 > files="$2"
156 > files="$2"
157 > opts=$3
157 > opts=$3
158 > echo "% $files: $msg"
158 > echo "% $files: $msg"
159 > prefix=`echo "$files" | sed -e 's/ /-/g'`
159 > prefix=`echo "$files" | sed -e 's/ /-/g'`
160 > fmap="$prefix.fmap"
160 > fmap="$prefix.fmap"
161 > repo="$prefix.repo"
161 > repo="$prefix.repo"
162 > for i in $files; do
162 > for i in $files; do
163 > echo "include $i" >> "$fmap"
163 > echo "include $i" >> "$fmap"
164 > done
164 > done
165 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
165 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
166 > hg up -q -R "$repo"
166 > hg up -q -R "$repo"
167 > glog -R "$repo"
167 > glog -R "$repo"
168 > hg -R "$repo" manifest --debug
168 > hg -R "$repo" manifest --debug
169 > }
169 > }
170
170
171 full conversion
171 full conversion
172
172
173 $ hg convert --datesort git-repo2 fullrepo \
173 $ hg convert --datesort git-repo2 fullrepo \
174 > --config extensions.progress= --config progress.assume-tty=1 \
174 > --config extensions.progress= --config progress.assume-tty=1 \
175 > --config progress.delay=0 --config progress.changedelay=0 \
175 > --config progress.delay=0 --config progress.changedelay=0 \
176 > --config progress.refresh=0 --config progress.width=60
176 > --config progress.refresh=0 --config progress.width=60
177 \r (no-eol) (esc)
177 \r (no-eol) (esc)
178 scanning [===> ] 1/9\r (no-eol) (esc)
178 scanning [===> ] 1/9\r (no-eol) (esc)
179 scanning [========> ] 2/9\r (no-eol) (esc)
179 scanning [========> ] 2/9\r (no-eol) (esc)
180 scanning [=============> ] 3/9\r (no-eol) (esc)
180 scanning [=============> ] 3/9\r (no-eol) (esc)
181 scanning [==================> ] 4/9\r (no-eol) (esc)
181 scanning [==================> ] 4/9\r (no-eol) (esc)
182 scanning [=======================> ] 5/9\r (no-eol) (esc)
182 scanning [=======================> ] 5/9\r (no-eol) (esc)
183 scanning [============================> ] 6/9\r (no-eol) (esc)
183 scanning [============================> ] 6/9\r (no-eol) (esc)
184 scanning [=================================> ] 7/9\r (no-eol) (esc)
184 scanning [=================================> ] 7/9\r (no-eol) (esc)
185 scanning [======================================> ] 8/9\r (no-eol) (esc)
185 scanning [======================================> ] 8/9\r (no-eol) (esc)
186 scanning [===========================================>] 9/9\r (no-eol) (esc)
186 scanning [===========================================>] 9/9\r (no-eol) (esc)
187 \r (no-eol) (esc)
187 \r (no-eol) (esc)
188 \r (no-eol) (esc)
188 \r (no-eol) (esc)
189 converting [ ] 0/9\r (no-eol) (esc)
189 converting [ ] 0/9\r (no-eol) (esc)
190 getting files [======================================>] 1/1\r (no-eol) (esc)
190 getting files [======================================>] 1/1\r (no-eol) (esc)
191 \r (no-eol) (esc)
191 \r (no-eol) (esc)
192 \r (no-eol) (esc)
192 \r (no-eol) (esc)
193 converting [===> ] 1/9\r (no-eol) (esc)
193 converting [===> ] 1/9\r (no-eol) (esc)
194 getting files [======================================>] 1/1\r (no-eol) (esc)
194 getting files [======================================>] 1/1\r (no-eol) (esc)
195 \r (no-eol) (esc)
195 \r (no-eol) (esc)
196 \r (no-eol) (esc)
196 \r (no-eol) (esc)
197 converting [========> ] 2/9\r (no-eol) (esc)
197 converting [========> ] 2/9\r (no-eol) (esc)
198 getting files [======================================>] 1/1\r (no-eol) (esc)
198 getting files [======================================>] 1/1\r (no-eol) (esc)
199 \r (no-eol) (esc)
199 \r (no-eol) (esc)
200 \r (no-eol) (esc)
200 \r (no-eol) (esc)
201 converting [=============> ] 3/9\r (no-eol) (esc)
201 converting [=============> ] 3/9\r (no-eol) (esc)
202 getting files [======================================>] 1/1\r (no-eol) (esc)
202 getting files [======================================>] 1/1\r (no-eol) (esc)
203 \r (no-eol) (esc)
203 \r (no-eol) (esc)
204 \r (no-eol) (esc)
204 \r (no-eol) (esc)
205 converting [=================> ] 4/9\r (no-eol) (esc)
205 converting [=================> ] 4/9\r (no-eol) (esc)
206 getting files [======================================>] 1/1\r (no-eol) (esc)
206 getting files [======================================>] 1/1\r (no-eol) (esc)
207 \r (no-eol) (esc)
207 \r (no-eol) (esc)
208 \r (no-eol) (esc)
208 \r (no-eol) (esc)
209 converting [======================> ] 5/9\r (no-eol) (esc)
209 converting [======================> ] 5/9\r (no-eol) (esc)
210 getting files [===> ] 1/8\r (no-eol) (esc)
210 getting files [===> ] 1/8\r (no-eol) (esc)
211 getting files [========> ] 2/8\r (no-eol) (esc)
211 getting files [========> ] 2/8\r (no-eol) (esc)
212 getting files [=============> ] 3/8\r (no-eol) (esc)
212 getting files [=============> ] 3/8\r (no-eol) (esc)
213 getting files [==================> ] 4/8\r (no-eol) (esc)
213 getting files [==================> ] 4/8\r (no-eol) (esc)
214 getting files [=======================> ] 5/8\r (no-eol) (esc)
214 getting files [=======================> ] 5/8\r (no-eol) (esc)
215 getting files [============================> ] 6/8\r (no-eol) (esc)
215 getting files [============================> ] 6/8\r (no-eol) (esc)
216 getting files [=================================> ] 7/8\r (no-eol) (esc)
216 getting files [=================================> ] 7/8\r (no-eol) (esc)
217 getting files [======================================>] 8/8\r (no-eol) (esc)
217 getting files [======================================>] 8/8\r (no-eol) (esc)
218 \r (no-eol) (esc)
218 \r (no-eol) (esc)
219 \r (no-eol) (esc)
219 \r (no-eol) (esc)
220 converting [===========================> ] 6/9\r (no-eol) (esc)
220 converting [===========================> ] 6/9\r (no-eol) (esc)
221 getting files [======================================>] 1/1\r (no-eol) (esc)
221 getting files [======================================>] 1/1\r (no-eol) (esc)
222 \r (no-eol) (esc)
222 \r (no-eol) (esc)
223 \r (no-eol) (esc)
223 \r (no-eol) (esc)
224 converting [===============================> ] 7/9\r (no-eol) (esc)
224 converting [===============================> ] 7/9\r (no-eol) (esc)
225 getting files [======================================>] 1/1\r (no-eol) (esc)
225 getting files [======================================>] 1/1\r (no-eol) (esc)
226 \r (no-eol) (esc)
226 \r (no-eol) (esc)
227 \r (no-eol) (esc)
227 \r (no-eol) (esc)
228 converting [====================================> ] 8/9\r (no-eol) (esc)
228 converting [====================================> ] 8/9\r (no-eol) (esc)
229 getting files [==================> ] 1/2\r (no-eol) (esc)
229 getting files [==================> ] 1/2\r (no-eol) (esc)
230 getting files [======================================>] 2/2\r (no-eol) (esc)
230 getting files [======================================>] 2/2\r (no-eol) (esc)
231 \r (no-eol) (esc)
231 \r (no-eol) (esc)
232 initializing destination fullrepo repository
232 initializing destination fullrepo repository
233 scanning source...
233 scanning source...
234 sorting...
234 sorting...
235 converting...
235 converting...
236 8 add foo
236 8 add foo
237 7 change foo
237 7 change foo
238 6 add quux
238 6 add quux
239 5 add bar
239 5 add bar
240 4 add baz
240 4 add baz
241 3 Octopus merge
241 3 Octopus merge
242 2 change bar
242 2 change bar
243 1 change foo
243 1 change foo
244 0 Discard change to foo
244 0 Discard change to foo
245 updating bookmarks
245 updating bookmarks
246 $ hg up -q -R fullrepo
246 $ hg up -q -R fullrepo
247 $ glog -R fullrepo
247 $ glog -R fullrepo
248 @ 9 "Discard change to foo" files: foo
248 @ 9 "Discard change to foo" files: foo
249 |\
249 |\
250 | o 8 "change foo" files: foo
250 | o 8 "change foo" files: foo
251 | |
251 | |
252 o | 7 "change bar" files: bar
252 o | 7 "change bar" files: bar
253 |/
253 |/
254 o 6 "(octopus merge fixup)" files:
254 o 6 "(octopus merge fixup)" files:
255 |\
255 |\
256 | o 5 "Octopus merge" files: baz
256 | o 5 "Octopus merge" files: baz
257 | |\
257 | |\
258 o | | 4 "add baz" files: baz
258 o | | 4 "add baz" files: baz
259 | | |
259 | | |
260 +---o 3 "add bar" files: bar
260 +---o 3 "add bar" files: bar
261 | |
261 | |
262 o | 2 "add quux" files: quux
262 o | 2 "add quux" files: quux
263 | |
263 | |
264 | o 1 "change foo" files: foo
264 | o 1 "change foo" files: foo
265 |/
265 |/
266 o 0 "add foo" files: foo
266 o 0 "add foo" files: foo
267
267
268 $ hg -R fullrepo manifest --debug
268 $ hg -R fullrepo manifest --debug
269 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
269 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
270 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
270 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
271 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
271 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
272 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
272 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
273 $ splitrepo 'octopus merge' 'foo bar baz'
273 $ splitrepo 'octopus merge' 'foo bar baz'
274 % foo bar baz: octopus merge
274 % foo bar baz: octopus merge
275 @ 8 "Discard change to foo" files: foo
275 @ 8 "Discard change to foo" files: foo
276 |\
276 |\
277 | o 7 "change foo" files: foo
277 | o 7 "change foo" files: foo
278 | |
278 | |
279 o | 6 "change bar" files: bar
279 o | 6 "change bar" files: bar
280 |/
280 |/
281 o 5 "(octopus merge fixup)" files:
281 o 5 "(octopus merge fixup)" files:
282 |\
282 |\
283 | o 4 "Octopus merge" files: baz
283 | o 4 "Octopus merge" files: baz
284 | |\
284 | |\
285 o | | 3 "add baz" files: baz
285 o | | 3 "add baz" files: baz
286 | | |
286 | | |
287 +---o 2 "add bar" files: bar
287 +---o 2 "add bar" files: bar
288 | |
288 | |
289 | o 1 "change foo" files: foo
289 | o 1 "change foo" files: foo
290 |/
290 |/
291 o 0 "add foo" files: foo
291 o 0 "add foo" files: foo
292
292
293 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
293 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
294 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
294 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
295 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
295 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
296 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
296 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
297 % foo baz quux: only some parents of an octopus merge; "discard" a head
297 % foo baz quux: only some parents of an octopus merge; "discard" a head
298 @ 6 "Discard change to foo" files: foo
298 @ 6 "Discard change to foo" files: foo
299 |
299 |
300 o 5 "change foo" files: foo
300 o 5 "change foo" files: foo
301 |
301 |
302 o 4 "Octopus merge" files:
302 o 4 "Octopus merge" files:
303 |\
303 |\
304 | o 3 "add baz" files: baz
304 | o 3 "add baz" files: baz
305 | |
305 | |
306 | o 2 "add quux" files: quux
306 | o 2 "add quux" files: quux
307 | |
307 | |
308 o | 1 "change foo" files: foo
308 o | 1 "change foo" files: foo
309 |/
309 |/
310 o 0 "add foo" files: foo
310 o 0 "add foo" files: foo
311
311
312 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
312 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
313 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
313 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
314 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
314 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
315
315
316 test importing git renames and copies
316 test importing git renames and copies
317
317
318 $ cd git-repo2
318 $ cd git-repo2
319 $ git mv foo foo-renamed
319 $ git mv foo foo-renamed
320 since bar is not touched in this commit, this copy will not be detected
320 since bar is not touched in this commit, this copy will not be detected
321 $ cp bar bar-copied
321 $ cp bar bar-copied
322 $ cp baz baz-copied
322 $ cp baz baz-copied
323 $ cp baz baz-copied2
323 $ cp baz baz-copied2
324 $ cp baz ba-copy
324 $ cp baz ba-copy
325 $ echo baz2 >> baz
325 $ echo baz2 >> baz
326 $ git add bar-copied baz-copied baz-copied2 ba-copy
326 $ git add bar-copied baz-copied baz-copied2 ba-copy
327 $ commit -a -m 'rename and copy'
327 $ commit -a -m 'rename and copy'
328 $ cd ..
328 $ cd ..
329
329
330 input validation
330 input validation
331 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
331 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
332 abort: convert.git.similarity is not an integer ('foo')
332 abort: convert.git.similarity is not an integer ('foo')
333 [255]
333 [255]
334 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
334 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
335 abort: similarity must be between 0 and 100
335 abort: similarity must be between 0 and 100
336 [255]
336 [255]
337 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
337 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
338 abort: similarity must be between 0 and 100
338 abort: similarity must be between 0 and 100
339 [255]
339 [255]
340
340
341 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
341 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
342 $ hg -R fullrepo status -C --change master
342 $ hg -R fullrepo status -C --change master
343 M baz
343 M baz
344 A ba-copy
344 A ba-copy
345 baz
345 baz
346 A bar-copied
346 A bar-copied
347 A baz-copied
347 A baz-copied
348 baz
348 baz
349 A baz-copied2
349 A baz-copied2
350 baz
350 baz
351 A foo-renamed
351 A foo-renamed
352 foo
352 foo
353 R foo
353 R foo
354
354
355 Ensure that the modification to the copy source was preserved
355 Ensure that the modification to the copy source was preserved
356 (there was a bug where if the copy dest was alphabetically prior to the copy
356 (there was a bug where if the copy dest was alphabetically prior to the copy
357 source, the copy source took the contents of the copy dest)
357 source, the copy source took the contents of the copy dest)
358 $ hg cat -r tip fullrepo/baz
358 $ hg cat -r tip fullrepo/baz
359 baz
359 baz
360 baz2
360 baz2
361
361
362 $ cd git-repo2
362 $ cd git-repo2
363 $ echo bar2 >> bar
363 $ echo bar2 >> bar
364 $ commit -a -m 'change bar'
364 $ commit -a -m 'change bar'
365 $ cp bar bar-copied2
365 $ cp bar bar-copied2
366 $ git add bar-copied2
366 $ git add bar-copied2
367 $ commit -a -m 'copy with no changes'
367 $ commit -a -m 'copy with no changes'
368 $ cd ..
368 $ cd ..
369
369
370 $ hg -q convert --config convert.git.similarity=100 \
370 $ hg -q convert --config convert.git.similarity=100 \
371 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
371 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
372 $ hg -R fullrepo status -C --change master
372 $ hg -R fullrepo status -C --change master
373 A bar-copied2
373 A bar-copied2
374 bar
374 bar
375
375
376 test binary conversion (issue1359)
376 test binary conversion (issue1359)
377
377
378 $ count=19
378 $ count=19
379 $ mkdir git-repo3
379 $ mkdir git-repo3
380 $ cd git-repo3
380 $ cd git-repo3
381 $ git init-db >/dev/null 2>/dev/null
381 $ git init-db >/dev/null 2>/dev/null
382 $ $PYTHON -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
382 $ $PYTHON -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
383 $ git add b
383 $ git add b
384 $ commit -a -m addbinary
384 $ commit -a -m addbinary
385 $ cd ..
385 $ cd ..
386
386
387 convert binary file
387 convert binary file
388
388
389 $ hg convert git-repo3 git-repo3-hg
389 $ hg convert git-repo3 git-repo3-hg
390 initializing destination git-repo3-hg repository
390 initializing destination git-repo3-hg repository
391 scanning source...
391 scanning source...
392 sorting...
392 sorting...
393 converting...
393 converting...
394 0 addbinary
394 0 addbinary
395 updating bookmarks
395 updating bookmarks
396 $ cd git-repo3-hg
396 $ cd git-repo3-hg
397 $ hg up -C
397 $ hg up -C
398 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 $ $PYTHON -c 'print len(file("b", "rb").read())'
399 $ $PYTHON -c 'print len(file("b", "rb").read())'
400 4096
400 4096
401 $ cd ..
401 $ cd ..
402
402
403 test author vs committer
403 test author vs committer
404
404
405 $ mkdir git-repo4
405 $ mkdir git-repo4
406 $ cd git-repo4
406 $ cd git-repo4
407 $ git init-db >/dev/null 2>/dev/null
407 $ git init-db >/dev/null 2>/dev/null
408 $ echo >> foo
408 $ echo >> foo
409 $ git add foo
409 $ git add foo
410 $ commit -a -m addfoo
410 $ commit -a -m addfoo
411 $ echo >> foo
411 $ echo >> foo
412 $ GIT_AUTHOR_NAME="nottest"
412 $ GIT_AUTHOR_NAME="nottest"
413 $ commit -a -m addfoo2
413 $ commit -a -m addfoo2
414 $ cd ..
414 $ cd ..
415
415
416 convert author committer
416 convert author committer
417
417
418 $ hg convert git-repo4 git-repo4-hg
418 $ hg convert git-repo4 git-repo4-hg
419 initializing destination git-repo4-hg repository
419 initializing destination git-repo4-hg repository
420 scanning source...
420 scanning source...
421 sorting...
421 sorting...
422 converting...
422 converting...
423 1 addfoo
423 1 addfoo
424 0 addfoo2
424 0 addfoo2
425 updating bookmarks
425 updating bookmarks
426 $ hg -R git-repo4-hg log -v
426 $ hg -R git-repo4-hg log -v
427 changeset: 1:d63e967f93da
427 changeset: 1:d63e967f93da
428 bookmark: master
428 bookmark: master
429 tag: tip
429 tag: tip
430 user: nottest <test@example.org>
430 user: nottest <test@example.org>
431 date: Mon Jan 01 00:00:21 2007 +0000
431 date: Mon Jan 01 00:00:21 2007 +0000
432 files: foo
432 files: foo
433 description:
433 description:
434 addfoo2
434 addfoo2
435
435
436 committer: test <test@example.org>
436 committer: test <test@example.org>
437
437
438
438
439 changeset: 0:0735477b0224
439 changeset: 0:0735477b0224
440 user: test <test@example.org>
440 user: test <test@example.org>
441 date: Mon Jan 01 00:00:20 2007 +0000
441 date: Mon Jan 01 00:00:20 2007 +0000
442 files: foo
442 files: foo
443 description:
443 description:
444 addfoo
444 addfoo
445
445
446
446
447
447
448 --sourceorder should fail
448 --sourceorder should fail
449
449
450 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
450 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
451 initializing destination git-repo4-sourcesort-hg repository
451 initializing destination git-repo4-sourcesort-hg repository
452 abort: --sourcesort is not supported by this data source
452 abort: --sourcesort is not supported by this data source
453 [255]
453 [255]
454
454
455 test converting certain branches
455 test converting certain branches
456
456
457 $ mkdir git-testrevs
457 $ mkdir git-testrevs
458 $ cd git-testrevs
458 $ cd git-testrevs
459 $ git init
459 $ git init
460 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
460 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
461 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
461 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
462 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
462 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
463 $ git checkout -b goodbranch 'HEAD^'
463 $ git checkout -b goodbranch 'HEAD^'
464 Switched to a new branch 'goodbranch'
464 Switched to a new branch 'goodbranch'
465 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
465 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
466 $ git checkout -b badbranch 'HEAD^'
466 $ git checkout -b badbranch 'HEAD^'
467 Switched to a new branch 'badbranch'
467 Switched to a new branch 'badbranch'
468 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
468 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
469 $ cd ..
469 $ cd ..
470 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
470 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
471 initializing destination hg-testrevs repository
471 initializing destination hg-testrevs repository
472 scanning source...
472 scanning source...
473 sorting...
473 sorting...
474 converting...
474 converting...
475 2 first
475 2 first
476 1 good branch commit
476 1 good branch commit
477 0 master commit
477 0 master commit
478 updating bookmarks
478 updating bookmarks
479 $ cd hg-testrevs
479 $ cd hg-testrevs
480 $ hg log -G -T '{rev} {bookmarks}'
480 $ hg log -G -T '{rev} {bookmarks}'
481 o 2 master
481 o 2 master
482 |
482 |
483 | o 1 goodbranch
483 | o 1 goodbranch
484 |/
484 |/
485 o 0
485 o 0
486
486
487 $ cd ..
487 $ cd ..
488
488
489 test sub modules
489 test sub modules
490
490
491 $ mkdir git-repo5
491 $ mkdir git-repo5
492 $ cd git-repo5
492 $ cd git-repo5
493 $ git init-db >/dev/null 2>/dev/null
493 $ git init-db >/dev/null 2>/dev/null
494 $ echo 'sub' >> foo
494 $ echo 'sub' >> foo
495 $ git add foo
495 $ git add foo
496 $ commit -a -m 'addfoo'
496 $ commit -a -m 'addfoo'
497 $ BASE=`pwd`
497 $ BASE=`pwd`
498 $ cd ..
498 $ cd ..
499 $ mkdir git-repo6
499 $ mkdir git-repo6
500 $ cd git-repo6
500 $ cd git-repo6
501 $ git init-db >/dev/null 2>/dev/null
501 $ git init-db >/dev/null 2>/dev/null
502 $ git submodule add ${BASE} >/dev/null 2>/dev/null
502 $ git submodule add ${BASE} >/dev/null 2>/dev/null
503 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
503 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
504
504
505 test non-tab whitespace .gitmodules
505 test non-tab whitespace .gitmodules
506
506
507 $ cat >> .gitmodules <<EOF
507 $ cat >> .gitmodules <<EOF
508 > [submodule "git-repo5"]
508 > [submodule "git-repo5"]
509 > path = git-repo5
509 > path = git-repo5
510 > url = git-repo5
510 > url = git-repo5
511 > EOF
511 > EOF
512 $ git commit -q -a -m "weird white space submodule"
512 $ git commit -q -a -m "weird white space submodule"
513 $ cd ..
513 $ cd ..
514 $ hg convert git-repo6 hg-repo6
514 $ hg convert git-repo6 hg-repo6
515 initializing destination hg-repo6 repository
515 initializing destination hg-repo6 repository
516 scanning source...
516 scanning source...
517 sorting...
517 sorting...
518 converting...
518 converting...
519 1 addsubmodule
519 1 addsubmodule
520 0 weird white space submodule
520 0 weird white space submodule
521 updating bookmarks
521 updating bookmarks
522
522
523 $ rm -rf hg-repo6
523 $ rm -rf hg-repo6
524 $ cd git-repo6
524 $ cd git-repo6
525 $ git reset --hard 'HEAD^' > /dev/null
525 $ git reset --hard 'HEAD^' > /dev/null
526
526
527 test missing .gitmodules
527 test missing .gitmodules
528
528
529 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
529 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
530 $ git checkout HEAD .gitmodules
530 $ git checkout HEAD .gitmodules
531 $ git rm .gitmodules
531 $ git rm .gitmodules
532 rm '.gitmodules'
532 rm '.gitmodules'
533 $ git commit -q -m "remove .gitmodules" .gitmodules
533 $ git commit -q -m "remove .gitmodules" .gitmodules
534 $ git commit -q -m "missing .gitmodules"
534 $ git commit -q -m "missing .gitmodules"
535 $ cd ..
535 $ cd ..
536 $ hg convert git-repo6 hg-repo6 --traceback
536 $ hg convert git-repo6 hg-repo6 --traceback
537 fatal: Path '.gitmodules' does not exist in '*' (glob)
537 fatal: Path '.gitmodules' does not exist in '*' (glob)
538 initializing destination hg-repo6 repository
538 initializing destination hg-repo6 repository
539 scanning source...
539 scanning source...
540 sorting...
540 sorting...
541 converting...
541 converting...
542 2 addsubmodule
542 2 addsubmodule
543 1 remove .gitmodules
543 1 remove .gitmodules
544 0 missing .gitmodules
544 0 missing .gitmodules
545 warning: cannot read submodules config file in * (glob)
545 warning: cannot read submodules config file in * (glob)
546 updating bookmarks
546 updating bookmarks
547 $ rm -rf hg-repo6
547 $ rm -rf hg-repo6
548 $ cd git-repo6
548 $ cd git-repo6
549 $ rm -rf git-repo4
549 $ rm -rf git-repo4
550 $ git reset --hard 'HEAD^^' > /dev/null
550 $ git reset --hard 'HEAD^^' > /dev/null
551 $ cd ..
551 $ cd ..
552
552
553 test invalid splicemap1
553 test invalid splicemap1
554
554
555 $ cat > splicemap <<EOF
555 $ cat > splicemap <<EOF
556 > $VALIDID1
556 > $VALIDID1
557 > EOF
557 > EOF
558 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
558 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
559 initializing destination git-repo2-splicemap1-hg repository
559 initializing destination git-repo2-splicemap1-hg repository
560 abort: syntax error in splicemap(1): child parent1[,parent2] expected
560 abort: syntax error in splicemap(1): child parent1[,parent2] expected
561 [255]
561 [255]
562
562
563 test invalid splicemap2
563 test invalid splicemap2
564
564
565 $ cat > splicemap <<EOF
565 $ cat > splicemap <<EOF
566 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
566 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
567 > EOF
567 > EOF
568 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
568 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
569 initializing destination git-repo2-splicemap2-hg repository
569 initializing destination git-repo2-splicemap2-hg repository
570 abort: syntax error in splicemap(1): child parent1[,parent2] expected
570 abort: syntax error in splicemap(1): child parent1[,parent2] expected
571 [255]
571 [255]
572
572
573 test invalid splicemap3
573 test invalid splicemap3
574
574
575 $ cat > splicemap <<EOF
575 $ cat > splicemap <<EOF
576 > $INVALIDID1 $INVALIDID2
576 > $INVALIDID1 $INVALIDID2
577 > EOF
577 > EOF
578 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
578 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
579 initializing destination git-repo2-splicemap3-hg repository
579 initializing destination git-repo2-splicemap3-hg repository
580 abort: splicemap entry afd12345af is not a valid revision identifier
580 abort: splicemap entry afd12345af is not a valid revision identifier
581 [255]
581 [255]
582
582
583 convert sub modules
583 convert sub modules
584 $ hg convert git-repo6 git-repo6-hg
584 $ hg convert git-repo6 git-repo6-hg
585 initializing destination git-repo6-hg repository
585 initializing destination git-repo6-hg repository
586 scanning source...
586 scanning source...
587 sorting...
587 sorting...
588 converting...
588 converting...
589 0 addsubmodule
589 0 addsubmodule
590 updating bookmarks
590 updating bookmarks
591 $ hg -R git-repo6-hg log -v
591 $ hg -R git-repo6-hg log -v
592 changeset: 0:* (glob)
592 changeset: 0:* (glob)
593 bookmark: master
593 bookmark: master
594 tag: tip
594 tag: tip
595 user: nottest <test@example.org>
595 user: nottest <test@example.org>
596 date: Mon Jan 01 00:00:23 2007 +0000
596 date: Mon Jan 01 00:00:23 2007 +0000
597 files: .hgsub .hgsubstate
597 files: .hgsub .hgsubstate
598 description:
598 description:
599 addsubmodule
599 addsubmodule
600
600
601 committer: test <test@example.org>
601 committer: test <test@example.org>
602
602
603
603
604
604
605 $ cd git-repo6-hg
605 $ cd git-repo6-hg
606 $ hg up >/dev/null 2>/dev/null
606 $ hg up >/dev/null 2>/dev/null
607 $ cat .hgsubstate
607 $ cat .hgsubstate
608 * git-repo5 (glob)
608 * git-repo5 (glob)
609 $ cd git-repo5
609 $ cd git-repo5
610 $ cat foo
610 $ cat foo
611 sub
611 sub
612
612
613 $ cd ../..
613 $ cd ../..
614
614
615 make sure rename detection doesn't break removing and adding gitmodules
615 make sure rename detection doesn't break removing and adding gitmodules
616
616
617 $ cd git-repo6
617 $ cd git-repo6
618 $ git mv .gitmodules .gitmodules-renamed
618 $ git mv .gitmodules .gitmodules-renamed
619 $ commit -a -m 'rename .gitmodules'
619 $ commit -a -m 'rename .gitmodules'
620 $ git mv .gitmodules-renamed .gitmodules
620 $ git mv .gitmodules-renamed .gitmodules
621 $ commit -a -m 'rename .gitmodules back'
621 $ commit -a -m 'rename .gitmodules back'
622 $ cd ..
622 $ cd ..
623
623
624 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
624 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
625 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
625 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
626 rename .gitmodules
626 rename .gitmodules
627 $ hg -R git-repo6-hg status -C --change 'tip^'
627 $ hg -R git-repo6-hg status -C --change 'tip^'
628 A .gitmodules-renamed
628 A .gitmodules-renamed
629 R .hgsub
629 R .hgsub
630 R .hgsubstate
630 R .hgsubstate
631 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
631 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
632 rename .gitmodules back
632 rename .gitmodules back
633 $ hg -R git-repo6-hg status -C --change tip
633 $ hg -R git-repo6-hg status -C --change tip
634 A .hgsub
634 A .hgsub
635 A .hgsubstate
635 A .hgsubstate
636 R .gitmodules-renamed
636 R .gitmodules-renamed
637
637
638 convert the revision removing '.gitmodules' itself (and related
638 convert the revision removing '.gitmodules' itself (and related
639 submodules)
639 submodules)
640
640
641 $ cd git-repo6
641 $ cd git-repo6
642 $ git rm .gitmodules
642 $ git rm .gitmodules
643 rm '.gitmodules'
643 rm '.gitmodules'
644 $ git rm --cached git-repo5
644 $ git rm --cached git-repo5
645 rm 'git-repo5'
645 rm 'git-repo5'
646 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
646 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
647 $ cd ..
647 $ cd ..
648
648
649 $ hg convert -q git-repo6 git-repo6-hg
649 $ hg convert -q git-repo6 git-repo6-hg
650 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
650 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
651 remove .gitmodules and submodule git-repo5
651 remove .gitmodules and submodule git-repo5
652 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
652 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
653 .hgsub .hgsubstate
653 .hgsub .hgsubstate
654
654
655 skip submodules in the conversion
656
657 $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
658 $ hg -R no-submodules manifest --all
659 .gitmodules-renamed
660
655 convert using a different remote prefix
661 convert using a different remote prefix
656 $ git init git-repo7
662 $ git init git-repo7
657 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
663 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
658 $ cd git-repo7
664 $ cd git-repo7
659 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
665 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
660 git output variance, but that doesn't currently work in the middle of
666 git output variance, but that doesn't currently work in the middle of
661 a block, so do this for now.
667 a block, so do this for now.
662 $ touch a && git add a && git commit -am "commit a" | grep -v changed
668 $ touch a && git add a && git commit -am "commit a" | grep -v changed
663 [master (root-commit) 8ae5f69] commit a
669 [master (root-commit) 8ae5f69] commit a
664 Author: nottest <test@example.org>
670 Author: nottest <test@example.org>
665 create mode 100644 a
671 create mode 100644 a
666 $ cd ..
672 $ cd ..
667 $ git clone git-repo7 git-repo7-client
673 $ git clone git-repo7 git-repo7-client
668 Cloning into 'git-repo7-client'...
674 Cloning into 'git-repo7-client'...
669 done.
675 done.
670 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
676 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
671 initializing destination hg-repo7 repository
677 initializing destination hg-repo7 repository
672 scanning source...
678 scanning source...
673 sorting...
679 sorting...
674 converting...
680 converting...
675 0 commit a
681 0 commit a
676 updating bookmarks
682 updating bookmarks
677 $ hg -R hg-repo7 bookmarks
683 $ hg -R hg-repo7 bookmarks
678 master 0:03bf38caa4c6
684 master 0:03bf38caa4c6
679 origin/master 0:03bf38caa4c6
685 origin/master 0:03bf38caa4c6
680
686
681 Run convert when the remote branches have changed
687 Run convert when the remote branches have changed
682 (there was an old bug where the local convert read branches from the server)
688 (there was an old bug where the local convert read branches from the server)
683
689
684 $ cd git-repo7
690 $ cd git-repo7
685 $ echo a >> a
691 $ echo a >> a
686 $ git commit -am "move master forward"
692 $ git commit -am "move master forward"
687 [master 0c81947] move master forward
693 [master 0c81947] move master forward
688 Author: nottest <test@example.org>
694 Author: nottest <test@example.org>
689 1 file changed, 1 insertion(+)
695 1 file changed, 1 insertion(+)
690 $ cd ..
696 $ cd ..
691 $ rm -rf hg-repo7
697 $ rm -rf hg-repo7
692 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
698 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
693 initializing destination hg-repo7 repository
699 initializing destination hg-repo7 repository
694 scanning source...
700 scanning source...
695 sorting...
701 sorting...
696 converting...
702 converting...
697 0 commit a
703 0 commit a
698 updating bookmarks
704 updating bookmarks
699 $ hg -R hg-repo7 bookmarks
705 $ hg -R hg-repo7 bookmarks
700 master 0:03bf38caa4c6
706 master 0:03bf38caa4c6
701 origin/master 0:03bf38caa4c6
707 origin/master 0:03bf38caa4c6
702
708
703 damaged git repository tests:
709 damaged git repository tests:
704 In case the hard-coded hashes change, the following commands can be used to
710 In case the hard-coded hashes change, the following commands can be used to
705 list the hashes and their corresponding types in the repository:
711 list the hashes and their corresponding types in the repository:
706 cd git-repo4/.git/objects
712 cd git-repo4/.git/objects
707 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
713 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
708 cd ../../..
714 cd ../../..
709
715
710 damage git repository by renaming a commit object
716 damage git repository by renaming a commit object
711 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
717 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
712 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
718 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
713 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
719 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
714 abort: cannot read tags from git-repo4/.git
720 abort: cannot read tags from git-repo4/.git
715 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
721 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
716 damage git repository by renaming a blob object
722 damage git repository by renaming a blob object
717
723
718 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
724 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
719 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
725 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
720 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
726 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
721 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
727 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
722 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
728 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
723 damage git repository by renaming a tree object
729 damage git repository by renaming a tree object
724
730
725 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
731 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
726 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
732 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
727 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
733 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
728 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
734 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
@@ -1,532 +1,535 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.startrev
146 convert.hg.startrev
147 specify the initial Mercurial revision. The default is 0.
147 specify the initial Mercurial revision. The default is 0.
148 convert.hg.revs
148 convert.hg.revs
149 revset specifying the source revisions to convert.
149 revset specifying the source revisions to convert.
150
150
151 CVS Source
151 CVS Source
152 ##########
152 ##########
153
153
154 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
154 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
155 indicate the starting point of what will be converted. Direct access to
155 indicate the starting point of what will be converted. Direct access to
156 the repository files is not needed, unless of course the repository is
156 the repository files is not needed, unless of course the repository is
157 ":local:". The conversion uses the top level directory in the sandbox to
157 ":local:". The conversion uses the top level directory in the sandbox to
158 find the CVS repository, and then uses CVS rlog commands to find files to
158 find the CVS repository, and then uses CVS rlog commands to find files to
159 convert. This means that unless a filemap is given, all files under the
159 convert. This means that unless a filemap is given, all files under the
160 starting directory will be converted, and that any directory
160 starting directory will be converted, and that any directory
161 reorganization in the CVS sandbox is ignored.
161 reorganization in the CVS sandbox is ignored.
162
162
163 The following options can be used with "--config":
163 The following options can be used with "--config":
164
164
165 convert.cvsps.cache
165 convert.cvsps.cache
166 Set to False to disable remote log caching, for testing and
166 Set to False to disable remote log caching, for testing and
167 debugging purposes. Default is True.
167 debugging purposes. Default is True.
168 convert.cvsps.fuzz
168 convert.cvsps.fuzz
169 Specify the maximum time (in seconds) that is allowed
169 Specify the maximum time (in seconds) that is allowed
170 between commits with identical user and log message in a
170 between commits with identical user and log message in a
171 single changeset. When very large files were checked in as
171 single changeset. When very large files were checked in as
172 part of a changeset then the default may not be long enough.
172 part of a changeset then the default may not be long enough.
173 The default is 60.
173 The default is 60.
174 convert.cvsps.mergeto
174 convert.cvsps.mergeto
175 Specify a regular expression to which commit log messages
175 Specify a regular expression to which commit log messages
176 are matched. If a match occurs, then the conversion process
176 are matched. If a match occurs, then the conversion process
177 will insert a dummy revision merging the branch on which
177 will insert a dummy revision merging the branch on which
178 this log message occurs to the branch indicated in the
178 this log message occurs to the branch indicated in the
179 regex. Default is "{{mergetobranch ([-\w]+)}}"
179 regex. Default is "{{mergetobranch ([-\w]+)}}"
180 convert.cvsps.mergefrom
180 convert.cvsps.mergefrom
181 Specify a regular expression to which commit log messages
181 Specify a regular expression to which commit log messages
182 are matched. If a match occurs, then the conversion process
182 are matched. If a match occurs, then the conversion process
183 will add the most recent revision on the branch indicated in
183 will add the most recent revision on the branch indicated in
184 the regex as the second parent of the changeset. Default is
184 the regex as the second parent of the changeset. Default is
185 "{{mergefrombranch ([-\w]+)}}"
185 "{{mergefrombranch ([-\w]+)}}"
186 convert.localtimezone
186 convert.localtimezone
187 use local time (as determined by the TZ environment
187 use local time (as determined by the TZ environment
188 variable) for changeset date/times. The default is False
188 variable) for changeset date/times. The default is False
189 (use UTC).
189 (use UTC).
190 hooks.cvslog Specify a Python function to be called at the end of
190 hooks.cvslog Specify a Python function to be called at the end of
191 gathering the CVS log. The function is passed a list with
191 gathering the CVS log. The function is passed a list with
192 the log entries, and can modify the entries in-place, or add
192 the log entries, and can modify the entries in-place, or add
193 or delete them.
193 or delete them.
194 hooks.cvschangesets
194 hooks.cvschangesets
195 Specify a Python function to be called after the changesets
195 Specify a Python function to be called after the changesets
196 are calculated from the CVS log. The function is passed a
196 are calculated from the CVS log. The function is passed a
197 list with the changeset entries, and can modify the
197 list with the changeset entries, and can modify the
198 changesets in-place, or add or delete them.
198 changesets in-place, or add or delete them.
199
199
200 An additional "debugcvsps" Mercurial command allows the builtin changeset
200 An additional "debugcvsps" Mercurial command allows the builtin changeset
201 merging code to be run without doing a conversion. Its parameters and
201 merging code to be run without doing a conversion. Its parameters and
202 output are similar to that of cvsps 2.1. Please see the command help for
202 output are similar to that of cvsps 2.1. Please see the command help for
203 more details.
203 more details.
204
204
205 Subversion Source
205 Subversion Source
206 #################
206 #################
207
207
208 Subversion source detects classical trunk/branches/tags layouts. By
208 Subversion source detects classical trunk/branches/tags layouts. By
209 default, the supplied "svn://repo/path/" source URL is converted as a
209 default, the supplied "svn://repo/path/" source URL is converted as a
210 single branch. If "svn://repo/path/trunk" exists it replaces the default
210 single branch. If "svn://repo/path/trunk" exists it replaces the default
211 branch. If "svn://repo/path/branches" exists, its subdirectories are
211 branch. If "svn://repo/path/branches" exists, its subdirectories are
212 listed as possible branches. If "svn://repo/path/tags" exists, it is
212 listed as possible branches. If "svn://repo/path/tags" exists, it is
213 looked for tags referencing converted branches. Default "trunk",
213 looked for tags referencing converted branches. Default "trunk",
214 "branches" and "tags" values can be overridden with following options. Set
214 "branches" and "tags" values can be overridden with following options. Set
215 them to paths relative to the source URL, or leave them blank to disable
215 them to paths relative to the source URL, or leave them blank to disable
216 auto detection.
216 auto detection.
217
217
218 The following options can be set with "--config":
218 The following options can be set with "--config":
219
219
220 convert.svn.branches
220 convert.svn.branches
221 specify the directory containing branches. The default is
221 specify the directory containing branches. The default is
222 "branches".
222 "branches".
223 convert.svn.tags
223 convert.svn.tags
224 specify the directory containing tags. The default is
224 specify the directory containing tags. The default is
225 "tags".
225 "tags".
226 convert.svn.trunk
226 convert.svn.trunk
227 specify the name of the trunk branch. The default is
227 specify the name of the trunk branch. The default is
228 "trunk".
228 "trunk".
229 convert.localtimezone
229 convert.localtimezone
230 use local time (as determined by the TZ environment
230 use local time (as determined by the TZ environment
231 variable) for changeset date/times. The default is False
231 variable) for changeset date/times. The default is False
232 (use UTC).
232 (use UTC).
233
233
234 Source history can be retrieved starting at a specific revision, instead
234 Source history can be retrieved starting at a specific revision, instead
235 of being integrally converted. Only single branch conversions are
235 of being integrally converted. Only single branch conversions are
236 supported.
236 supported.
237
237
238 convert.svn.startrev
238 convert.svn.startrev
239 specify start Subversion revision number. The default is 0.
239 specify start Subversion revision number. The default is 0.
240
240
241 Git Source
241 Git Source
242 ##########
242 ##########
243
243
244 The Git importer converts commits from all reachable branches (refs in
244 The Git importer converts commits from all reachable branches (refs in
245 refs/heads) and remotes (refs in refs/remotes) to Mercurial. Branches are
245 refs/heads) and remotes (refs in refs/remotes) to Mercurial. Branches are
246 converted to bookmarks with the same name, with the leading 'refs/heads'
246 converted to bookmarks with the same name, with the leading 'refs/heads'
247 stripped. Git submodules are converted to Git subrepos in Mercurial.
247 stripped. Git submodules are converted to Git subrepos in Mercurial.
248
248
249 The following options can be set with "--config":
249 The following options can be set with "--config":
250
250
251 convert.git.similarity
251 convert.git.similarity
252 specify how similar files modified in a commit must be to be
252 specify how similar files modified in a commit must be to be
253 imported as renames or copies, as a percentage between "0"
253 imported as renames or copies, as a percentage between "0"
254 (disabled) and "100" (files must be identical). For example,
254 (disabled) and "100" (files must be identical). For example,
255 "90" means that a delete/add pair will be imported as a
255 "90" means that a delete/add pair will be imported as a
256 rename if more than 90% of the file hasn't changed. The
256 rename if more than 90% of the file hasn't changed. The
257 default is "50".
257 default is "50".
258 convert.git.findcopiesharder
258 convert.git.findcopiesharder
259 while detecting copies, look at all files in the working
259 while detecting copies, look at all files in the working
260 copy instead of just changed ones. This is very expensive
260 copy instead of just changed ones. This is very expensive
261 for large projects, and is only effective when
261 for large projects, and is only effective when
262 "convert.git.similarity" is greater than 0. The default is
262 "convert.git.similarity" is greater than 0. The default is
263 False.
263 False.
264 convert.git.remoteprefix
264 convert.git.remoteprefix
265 remote refs are converted as bookmarks with
265 remote refs are converted as bookmarks with
266 "convert.git.remoteprefix" as a prefix followed by a /. The
266 "convert.git.remoteprefix" as a prefix followed by a /. The
267 default is 'remote'.
267 default is 'remote'.
268 convert.git.skipsubmodules
269 does not convert root level .gitmodules files or files with
270 160000 mode indicating a submodule. Default is False.
268
271
269 Perforce Source
272 Perforce Source
270 ###############
273 ###############
271
274
272 The Perforce (P4) importer can be given a p4 depot path or a client
275 The Perforce (P4) importer can be given a p4 depot path or a client
273 specification as source. It will convert all files in the source to a flat
276 specification as source. It will convert all files in the source to a flat
274 Mercurial repository, ignoring labels, branches and integrations. Note
277 Mercurial repository, ignoring labels, branches and integrations. Note
275 that when a depot path is given you then usually should specify a target
278 that when a depot path is given you then usually should specify a target
276 directory, because otherwise the target may be named "...-hg".
279 directory, because otherwise the target may be named "...-hg".
277
280
278 The following options can be set with "--config":
281 The following options can be set with "--config":
279
282
280 convert.p4.encoding
283 convert.p4.encoding
281 specify the encoding to use when decoding standard output of
284 specify the encoding to use when decoding standard output of
282 the Perforce command line tool. The default is default
285 the Perforce command line tool. The default is default
283 system encoding.
286 system encoding.
284 convert.p4.startrev
287 convert.p4.startrev
285 specify initial Perforce revision (a Perforce changelist
288 specify initial Perforce revision (a Perforce changelist
286 number).
289 number).
287
290
288 Mercurial Destination
291 Mercurial Destination
289 #####################
292 #####################
290
293
291 The Mercurial destination will recognize Mercurial subrepositories in the
294 The Mercurial destination will recognize Mercurial subrepositories in the
292 destination directory, and update the .hgsubstate file automatically if
295 destination directory, and update the .hgsubstate file automatically if
293 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
296 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
294 Converting a repository with subrepositories requires converting a single
297 Converting a repository with subrepositories requires converting a single
295 repository at a time, from the bottom up.
298 repository at a time, from the bottom up.
296
299
297 The following options are supported:
300 The following options are supported:
298
301
299 convert.hg.clonebranches
302 convert.hg.clonebranches
300 dispatch source branches in separate clones. The default is
303 dispatch source branches in separate clones. The default is
301 False.
304 False.
302 convert.hg.tagsbranch
305 convert.hg.tagsbranch
303 branch name for tag revisions, defaults to "default".
306 branch name for tag revisions, defaults to "default".
304 convert.hg.usebranchnames
307 convert.hg.usebranchnames
305 preserve branch names. The default is True.
308 preserve branch names. The default is True.
306 convert.hg.sourcename
309 convert.hg.sourcename
307 records the given string as a 'convert_source' extra value
310 records the given string as a 'convert_source' extra value
308 on each commit made in the target repository. The default is
311 on each commit made in the target repository. The default is
309 None.
312 None.
310
313
311 All Destinations
314 All Destinations
312 ################
315 ################
313
316
314 All destination types accept the following options:
317 All destination types accept the following options:
315
318
316 convert.skiptags
319 convert.skiptags
317 does not convert tags from the source repo to the target
320 does not convert tags from the source repo to the target
318 repo. The default is False.
321 repo. The default is False.
319
322
320 options ([+] can be repeated):
323 options ([+] can be repeated):
321
324
322 -s --source-type TYPE source repository type
325 -s --source-type TYPE source repository type
323 -d --dest-type TYPE destination repository type
326 -d --dest-type TYPE destination repository type
324 -r --rev REV [+] import up to source revision REV
327 -r --rev REV [+] import up to source revision REV
325 -A --authormap FILE remap usernames using this file
328 -A --authormap FILE remap usernames using this file
326 --filemap FILE remap file names using contents of file
329 --filemap FILE remap file names using contents of file
327 --full apply filemap changes by converting all files again
330 --full apply filemap changes by converting all files again
328 --splicemap FILE splice synthesized history into place
331 --splicemap FILE splice synthesized history into place
329 --branchmap FILE change branch names while converting
332 --branchmap FILE change branch names while converting
330 --branchsort try to sort changesets by branches
333 --branchsort try to sort changesets by branches
331 --datesort try to sort changesets by date
334 --datesort try to sort changesets by date
332 --sourcesort preserve source changesets order
335 --sourcesort preserve source changesets order
333 --closesort try to reorder closed revisions
336 --closesort try to reorder closed revisions
334
337
335 (some details hidden, use --verbose to show complete help)
338 (some details hidden, use --verbose to show complete help)
336 $ hg init a
339 $ hg init a
337 $ cd a
340 $ cd a
338 $ echo a > a
341 $ echo a > a
339 $ hg ci -d'0 0' -Ama
342 $ hg ci -d'0 0' -Ama
340 adding a
343 adding a
341 $ hg cp a b
344 $ hg cp a b
342 $ hg ci -d'1 0' -mb
345 $ hg ci -d'1 0' -mb
343 $ hg rm a
346 $ hg rm a
344 $ hg ci -d'2 0' -mc
347 $ hg ci -d'2 0' -mc
345 $ hg mv b a
348 $ hg mv b a
346 $ hg ci -d'3 0' -md
349 $ hg ci -d'3 0' -md
347 $ echo a >> a
350 $ echo a >> a
348 $ hg ci -d'4 0' -me
351 $ hg ci -d'4 0' -me
349 $ cd ..
352 $ cd ..
350 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
353 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
351 assuming destination a-hg
354 assuming destination a-hg
352 initializing destination a-hg repository
355 initializing destination a-hg repository
353 scanning source...
356 scanning source...
354 sorting...
357 sorting...
355 converting...
358 converting...
356 4 a
359 4 a
357 3 b
360 3 b
358 2 c
361 2 c
359 1 d
362 1 d
360 0 e
363 0 e
361 $ hg --cwd a-hg pull ../a
364 $ hg --cwd a-hg pull ../a
362 pulling from ../a
365 pulling from ../a
363 searching for changes
366 searching for changes
364 no changes found
367 no changes found
365
368
366 conversion to existing file should fail
369 conversion to existing file should fail
367
370
368 $ touch bogusfile
371 $ touch bogusfile
369 $ hg convert a bogusfile
372 $ hg convert a bogusfile
370 initializing destination bogusfile repository
373 initializing destination bogusfile repository
371 abort: cannot create new bundle repository
374 abort: cannot create new bundle repository
372 [255]
375 [255]
373
376
374 #if unix-permissions no-root
377 #if unix-permissions no-root
375
378
376 conversion to dir without permissions should fail
379 conversion to dir without permissions should fail
377
380
378 $ mkdir bogusdir
381 $ mkdir bogusdir
379 $ chmod 000 bogusdir
382 $ chmod 000 bogusdir
380
383
381 $ hg convert a bogusdir
384 $ hg convert a bogusdir
382 abort: Permission denied: 'bogusdir'
385 abort: Permission denied: 'bogusdir'
383 [255]
386 [255]
384
387
385 user permissions should succeed
388 user permissions should succeed
386
389
387 $ chmod 700 bogusdir
390 $ chmod 700 bogusdir
388 $ hg convert a bogusdir
391 $ hg convert a bogusdir
389 initializing destination bogusdir repository
392 initializing destination bogusdir repository
390 scanning source...
393 scanning source...
391 sorting...
394 sorting...
392 converting...
395 converting...
393 4 a
396 4 a
394 3 b
397 3 b
395 2 c
398 2 c
396 1 d
399 1 d
397 0 e
400 0 e
398
401
399 #endif
402 #endif
400
403
401 test pre and post conversion actions
404 test pre and post conversion actions
402
405
403 $ echo 'include b' > filemap
406 $ echo 'include b' > filemap
404 $ hg convert --debug --filemap filemap a partialb | \
407 $ hg convert --debug --filemap filemap a partialb | \
405 > grep 'run hg'
408 > grep 'run hg'
406 run hg source pre-conversion action
409 run hg source pre-conversion action
407 run hg sink pre-conversion action
410 run hg sink pre-conversion action
408 run hg sink post-conversion action
411 run hg sink post-conversion action
409 run hg source post-conversion action
412 run hg source post-conversion action
410
413
411 converting empty dir should fail "nicely
414 converting empty dir should fail "nicely
412
415
413 $ mkdir emptydir
416 $ mkdir emptydir
414
417
415 override $PATH to ensure p4 not visible; use $PYTHON in case we're
418 override $PATH to ensure p4 not visible; use $PYTHON in case we're
416 running from a devel copy, not a temp installation
419 running from a devel copy, not a temp installation
417
420
418 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
421 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
419 assuming destination emptydir-hg
422 assuming destination emptydir-hg
420 initializing destination emptydir-hg repository
423 initializing destination emptydir-hg repository
421 emptydir does not look like a CVS checkout
424 emptydir does not look like a CVS checkout
422 emptydir does not look like a Git repository
425 emptydir does not look like a Git repository
423 emptydir does not look like a Subversion repository
426 emptydir does not look like a Subversion repository
424 emptydir is not a local Mercurial repository
427 emptydir is not a local Mercurial repository
425 emptydir does not look like a darcs repository
428 emptydir does not look like a darcs repository
426 emptydir does not look like a monotone repository
429 emptydir does not look like a monotone repository
427 emptydir does not look like a GNU Arch repository
430 emptydir does not look like a GNU Arch repository
428 emptydir does not look like a Bazaar repository
431 emptydir does not look like a Bazaar repository
429 cannot find required "p4" tool
432 cannot find required "p4" tool
430 abort: emptydir: missing or unsupported repository
433 abort: emptydir: missing or unsupported repository
431 [255]
434 [255]
432
435
433 convert with imaginary source type
436 convert with imaginary source type
434
437
435 $ hg convert --source-type foo a a-foo
438 $ hg convert --source-type foo a a-foo
436 initializing destination a-foo repository
439 initializing destination a-foo repository
437 abort: foo: invalid source repository type
440 abort: foo: invalid source repository type
438 [255]
441 [255]
439
442
440 convert with imaginary sink type
443 convert with imaginary sink type
441
444
442 $ hg convert --dest-type foo a a-foo
445 $ hg convert --dest-type foo a a-foo
443 abort: foo: invalid destination repository type
446 abort: foo: invalid destination repository type
444 [255]
447 [255]
445
448
446 testing: convert must not produce duplicate entries in fncache
449 testing: convert must not produce duplicate entries in fncache
447
450
448 $ hg convert a b
451 $ hg convert a b
449 initializing destination b repository
452 initializing destination b repository
450 scanning source...
453 scanning source...
451 sorting...
454 sorting...
452 converting...
455 converting...
453 4 a
456 4 a
454 3 b
457 3 b
455 2 c
458 2 c
456 1 d
459 1 d
457 0 e
460 0 e
458
461
459 contents of fncache file:
462 contents of fncache file:
460
463
461 $ cat b/.hg/store/fncache | sort
464 $ cat b/.hg/store/fncache | sort
462 data/a.i
465 data/a.i
463 data/b.i
466 data/b.i
464
467
465 test bogus URL
468 test bogus URL
466
469
467 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
470 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
468 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
471 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
469 [255]
472 [255]
470
473
471 test revset converted() lookup
474 test revset converted() lookup
472
475
473 $ hg --config convert.hg.saverev=True convert a c
476 $ hg --config convert.hg.saverev=True convert a c
474 initializing destination c repository
477 initializing destination c repository
475 scanning source...
478 scanning source...
476 sorting...
479 sorting...
477 converting...
480 converting...
478 4 a
481 4 a
479 3 b
482 3 b
480 2 c
483 2 c
481 1 d
484 1 d
482 0 e
485 0 e
483 $ echo f > c/f
486 $ echo f > c/f
484 $ hg -R c ci -d'0 0' -Amf
487 $ hg -R c ci -d'0 0' -Amf
485 adding f
488 adding f
486 created new head
489 created new head
487 $ hg -R c log -r "converted(09d945a62ce6)"
490 $ hg -R c log -r "converted(09d945a62ce6)"
488 changeset: 1:98c3dd46a874
491 changeset: 1:98c3dd46a874
489 user: test
492 user: test
490 date: Thu Jan 01 00:00:01 1970 +0000
493 date: Thu Jan 01 00:00:01 1970 +0000
491 summary: b
494 summary: b
492
495
493 $ hg -R c log -r "converted()"
496 $ hg -R c log -r "converted()"
494 changeset: 0:31ed57b2037c
497 changeset: 0:31ed57b2037c
495 user: test
498 user: test
496 date: Thu Jan 01 00:00:00 1970 +0000
499 date: Thu Jan 01 00:00:00 1970 +0000
497 summary: a
500 summary: a
498
501
499 changeset: 1:98c3dd46a874
502 changeset: 1:98c3dd46a874
500 user: test
503 user: test
501 date: Thu Jan 01 00:00:01 1970 +0000
504 date: Thu Jan 01 00:00:01 1970 +0000
502 summary: b
505 summary: b
503
506
504 changeset: 2:3b9ca06ef716
507 changeset: 2:3b9ca06ef716
505 user: test
508 user: test
506 date: Thu Jan 01 00:00:02 1970 +0000
509 date: Thu Jan 01 00:00:02 1970 +0000
507 summary: c
510 summary: c
508
511
509 changeset: 3:4e0debd37cf2
512 changeset: 3:4e0debd37cf2
510 user: test
513 user: test
511 date: Thu Jan 01 00:00:03 1970 +0000
514 date: Thu Jan 01 00:00:03 1970 +0000
512 summary: d
515 summary: d
513
516
514 changeset: 4:9de3bc9349c5
517 changeset: 4:9de3bc9349c5
515 user: test
518 user: test
516 date: Thu Jan 01 00:00:04 1970 +0000
519 date: Thu Jan 01 00:00:04 1970 +0000
517 summary: e
520 summary: e
518
521
519
522
520 test specifying a sourcename
523 test specifying a sourcename
521 $ echo g > a/g
524 $ echo g > a/g
522 $ hg -R a ci -d'0 0' -Amg
525 $ hg -R a ci -d'0 0' -Amg
523 adding g
526 adding g
524 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
527 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
525 scanning source...
528 scanning source...
526 sorting...
529 sorting...
527 converting...
530 converting...
528 0 g
531 0 g
529 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
532 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
530 branch=default
533 branch=default
531 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
534 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
532 convert_source=mysource
535 convert_source=mysource
General Comments 0
You need to be logged in to leave comments. Login now