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