##// END OF EJS Templates
convert: remove "replacecommitter" action...
Gregory Szorc -
r30815:c5bf2e8e default
parent child Browse files
Show More
@@ -1,507 +1,504 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
323 :convert.git.renamelimit: perform rename and copy detection up to this
324 many changed files in a commit. Increasing this will make rename
324 many changed files in a commit. Increasing this will make rename
325 and copy detection more accurate but will significantly slow down
325 and copy detection more accurate but will significantly slow down
326 computation on large projects. The option is only relevant if
326 computation on large projects. The option is only relevant if
327 ``convert.git.similarity`` is greater than 0. The default is
327 ``convert.git.similarity`` is greater than 0. The default is
328 ``400``.
328 ``400``.
329
329
330 :convert.git.committeractions: list of actions to take when processing
330 :convert.git.committeractions: list of actions to take when processing
331 author and committer values.
331 author and committer values.
332
332
333 Git commits have separate author (who wrote the commit) and committer
333 Git commits have separate author (who wrote the commit) and committer
334 (who applied the commit) fields. Not all destinations support separate
334 (who applied the commit) fields. Not all destinations support separate
335 author and committer fields (including Mercurial). This config option
335 author and committer fields (including Mercurial). This config option
336 controls what to do with these author and committer fields during
336 controls what to do with these author and committer fields during
337 conversion.
337 conversion.
338
338
339 A value of ``messagedifferent`` will append a ``committer: ...``
339 A value of ``messagedifferent`` will append a ``committer: ...``
340 line to the commit message if the Git committer is different from the
340 line to the commit message if the Git committer is different from the
341 author. The prefix of that line can be specified using the syntax
341 author. The prefix of that line can be specified using the syntax
342 ``messagedifferent=<prefix>``. e.g. ``messagedifferent=git-committer:``.
342 ``messagedifferent=<prefix>``. e.g. ``messagedifferent=git-committer:``.
343 When a prefix is specified, a space will always be inserted between the
343 When a prefix is specified, a space will always be inserted between the
344 prefix and the value.
344 prefix and the value.
345
345
346 ``messagealways`` behaves like ``messagedifferent`` except it will
346 ``messagealways`` behaves like ``messagedifferent`` except it will
347 always result in a ``committer: ...`` line being appended to the commit
347 always result in a ``committer: ...`` line being appended to the commit
348 message. This value is mutually exclusive with ``messagedifferent``.
348 message. This value is mutually exclusive with ``messagedifferent``.
349
349
350 ``dropcommitter`` will remove references to the committer. Only
350 ``dropcommitter`` will remove references to the committer. Only
351 references to the author will remain. Actions that add references
351 references to the author will remain. Actions that add references
352 to the committer will have no effect when this is set.
352 to the committer will have no effect when this is set.
353
353
354 ``replaceauthor`` will replace the value of the author field with
354 ``replaceauthor`` will replace the value of the author field with
355 the committer. Other actions that add references to the committer
355 the committer. Other actions that add references to the committer
356 will still take effect when this is set.
356 will still take effect when this is set.
357
357
358 ``replacecommitter`` will replace the value of the committer field
359 with the author.
360
361 The default is ``messagedifferent``.
358 The default is ``messagedifferent``.
362
359
363 :convert.git.extrakeys: list of extra keys from commit metadata to copy to
360 :convert.git.extrakeys: list of extra keys from commit metadata to copy to
364 the destination. Some Git repositories store extra metadata in commits.
361 the destination. Some Git repositories store extra metadata in commits.
365 By default, this non-default metadata will be lost during conversion.
362 By default, this non-default metadata will be lost during conversion.
366 Setting this config option can retain that metadata. Some built-in
363 Setting this config option can retain that metadata. Some built-in
367 keys such as ``parent`` and ``branch`` are not allowed to be copied.
364 keys such as ``parent`` and ``branch`` are not allowed to be copied.
368
365
369 :convert.git.remoteprefix: remote refs are converted as bookmarks with
366 :convert.git.remoteprefix: remote refs are converted as bookmarks with
370 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
367 ``convert.git.remoteprefix`` as a prefix followed by a /. The default
371 is 'remote'.
368 is 'remote'.
372
369
373 :convert.git.saverev: whether to store the original Git commit ID in the
370 :convert.git.saverev: whether to store the original Git commit ID in the
374 metadata of the destination commit. The default is True.
371 metadata of the destination commit. The default is True.
375
372
376 :convert.git.skipsubmodules: does not convert root level .gitmodules files
373 :convert.git.skipsubmodules: does not convert root level .gitmodules files
377 or files with 160000 mode indicating a submodule. Default is False.
374 or files with 160000 mode indicating a submodule. Default is False.
378
375
379 Perforce Source
376 Perforce Source
380 ###############
377 ###############
381
378
382 The Perforce (P4) importer can be given a p4 depot path or a
379 The Perforce (P4) importer can be given a p4 depot path or a
383 client specification as source. It will convert all files in the
380 client specification as source. It will convert all files in the
384 source to a flat Mercurial repository, ignoring labels, branches
381 source to a flat Mercurial repository, ignoring labels, branches
385 and integrations. Note that when a depot path is given you then
382 and integrations. Note that when a depot path is given you then
386 usually should specify a target directory, because otherwise the
383 usually should specify a target directory, because otherwise the
387 target may be named ``...-hg``.
384 target may be named ``...-hg``.
388
385
389 The following options can be set with ``--config``:
386 The following options can be set with ``--config``:
390
387
391 :convert.p4.encoding: specify the encoding to use when decoding standard
388 :convert.p4.encoding: specify the encoding to use when decoding standard
392 output of the Perforce command line tool. The default is default system
389 output of the Perforce command line tool. The default is default system
393 encoding.
390 encoding.
394
391
395 :convert.p4.startrev: specify initial Perforce revision (a
392 :convert.p4.startrev: specify initial Perforce revision (a
396 Perforce changelist number).
393 Perforce changelist number).
397
394
398 Mercurial Destination
395 Mercurial Destination
399 #####################
396 #####################
400
397
401 The Mercurial destination will recognize Mercurial subrepositories in the
398 The Mercurial destination will recognize Mercurial subrepositories in the
402 destination directory, and update the .hgsubstate file automatically if the
399 destination directory, and update the .hgsubstate file automatically if the
403 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
400 destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
404 Converting a repository with subrepositories requires converting a single
401 Converting a repository with subrepositories requires converting a single
405 repository at a time, from the bottom up.
402 repository at a time, from the bottom up.
406
403
407 .. container:: verbose
404 .. container:: verbose
408
405
409 An example showing how to convert a repository with subrepositories::
406 An example showing how to convert a repository with subrepositories::
410
407
411 # so convert knows the type when it sees a non empty destination
408 # so convert knows the type when it sees a non empty destination
412 $ hg init converted
409 $ hg init converted
413
410
414 $ hg convert orig/sub1 converted/sub1
411 $ hg convert orig/sub1 converted/sub1
415 $ hg convert orig/sub2 converted/sub2
412 $ hg convert orig/sub2 converted/sub2
416 $ hg convert orig converted
413 $ hg convert orig converted
417
414
418 The following options are supported:
415 The following options are supported:
419
416
420 :convert.hg.clonebranches: dispatch source branches in separate
417 :convert.hg.clonebranches: dispatch source branches in separate
421 clones. The default is False.
418 clones. The default is False.
422
419
423 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
420 :convert.hg.tagsbranch: branch name for tag revisions, defaults to
424 ``default``.
421 ``default``.
425
422
426 :convert.hg.usebranchnames: preserve branch names. The default is
423 :convert.hg.usebranchnames: preserve branch names. The default is
427 True.
424 True.
428
425
429 :convert.hg.sourcename: records the given string as a 'convert_source' extra
426 :convert.hg.sourcename: records the given string as a 'convert_source' extra
430 value on each commit made in the target repository. The default is None.
427 value on each commit made in the target repository. The default is None.
431
428
432 All Destinations
429 All Destinations
433 ################
430 ################
434
431
435 All destination types accept the following options:
432 All destination types accept the following options:
436
433
437 :convert.skiptags: does not convert tags from the source repo to the target
434 :convert.skiptags: does not convert tags from the source repo to the target
438 repo. The default is False.
435 repo. The default is False.
439 """
436 """
440 return convcmd.convert(ui, src, dest, revmapfile, **opts)
437 return convcmd.convert(ui, src, dest, revmapfile, **opts)
441
438
442 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
439 @command('debugsvnlog', [], 'hg debugsvnlog', norepo=True)
443 def debugsvnlog(ui, **opts):
440 def debugsvnlog(ui, **opts):
444 return subversion.debugsvnlog(ui, **opts)
441 return subversion.debugsvnlog(ui, **opts)
445
442
446 @command('debugcvsps',
443 @command('debugcvsps',
447 [
444 [
448 # Main options shared with cvsps-2.1
445 # Main options shared with cvsps-2.1
449 ('b', 'branches', [], _('only return changes on specified branches')),
446 ('b', 'branches', [], _('only return changes on specified branches')),
450 ('p', 'prefix', '', _('prefix to remove from file names')),
447 ('p', 'prefix', '', _('prefix to remove from file names')),
451 ('r', 'revisions', [],
448 ('r', 'revisions', [],
452 _('only return changes after or between specified tags')),
449 _('only return changes after or between specified tags')),
453 ('u', 'update-cache', None, _("update cvs log cache")),
450 ('u', 'update-cache', None, _("update cvs log cache")),
454 ('x', 'new-cache', None, _("create new cvs log cache")),
451 ('x', 'new-cache', None, _("create new cvs log cache")),
455 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
452 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
456 ('', 'root', '', _('specify cvsroot')),
453 ('', 'root', '', _('specify cvsroot')),
457 # Options specific to builtin cvsps
454 # Options specific to builtin cvsps
458 ('', 'parents', '', _('show parent changesets')),
455 ('', 'parents', '', _('show parent changesets')),
459 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
456 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
460 # Options that are ignored for compatibility with cvsps-2.1
457 # Options that are ignored for compatibility with cvsps-2.1
461 ('A', 'cvs-direct', None, _('ignored for compatibility')),
458 ('A', 'cvs-direct', None, _('ignored for compatibility')),
462 ],
459 ],
463 _('hg debugcvsps [OPTION]... [PATH]...'),
460 _('hg debugcvsps [OPTION]... [PATH]...'),
464 norepo=True)
461 norepo=True)
465 def debugcvsps(ui, *args, **opts):
462 def debugcvsps(ui, *args, **opts):
466 '''create changeset information from CVS
463 '''create changeset information from CVS
467
464
468 This command is intended as a debugging tool for the CVS to
465 This command is intended as a debugging tool for the CVS to
469 Mercurial converter, and can be used as a direct replacement for
466 Mercurial converter, and can be used as a direct replacement for
470 cvsps.
467 cvsps.
471
468
472 Hg debugcvsps reads the CVS rlog for current directory (or any
469 Hg debugcvsps reads the CVS rlog for current directory (or any
473 named directory) in the CVS repository, and converts the log to a
470 named directory) in the CVS repository, and converts the log to a
474 series of changesets based on matching commit log entries and
471 series of changesets based on matching commit log entries and
475 dates.'''
472 dates.'''
476 return cvsps.debugcvsps(ui, *args, **opts)
473 return cvsps.debugcvsps(ui, *args, **opts)
477
474
478 def kwconverted(ctx, name):
475 def kwconverted(ctx, name):
479 rev = ctx.extra().get('convert_revision', '')
476 rev = ctx.extra().get('convert_revision', '')
480 if rev.startswith('svn:'):
477 if rev.startswith('svn:'):
481 if name == 'svnrev':
478 if name == 'svnrev':
482 return str(subversion.revsplit(rev)[2])
479 return str(subversion.revsplit(rev)[2])
483 elif name == 'svnpath':
480 elif name == 'svnpath':
484 return subversion.revsplit(rev)[1]
481 return subversion.revsplit(rev)[1]
485 elif name == 'svnuuid':
482 elif name == 'svnuuid':
486 return subversion.revsplit(rev)[0]
483 return subversion.revsplit(rev)[0]
487 return rev
484 return rev
488
485
489 templatekeyword = registrar.templatekeyword()
486 templatekeyword = registrar.templatekeyword()
490
487
491 @templatekeyword('svnrev')
488 @templatekeyword('svnrev')
492 def kwsvnrev(repo, ctx, **args):
489 def kwsvnrev(repo, ctx, **args):
493 """String. Converted subversion revision number."""
490 """String. Converted subversion revision number."""
494 return kwconverted(ctx, 'svnrev')
491 return kwconverted(ctx, 'svnrev')
495
492
496 @templatekeyword('svnpath')
493 @templatekeyword('svnpath')
497 def kwsvnpath(repo, ctx, **args):
494 def kwsvnpath(repo, ctx, **args):
498 """String. Converted subversion revision project path."""
495 """String. Converted subversion revision project path."""
499 return kwconverted(ctx, 'svnpath')
496 return kwconverted(ctx, 'svnpath')
500
497
501 @templatekeyword('svnuuid')
498 @templatekeyword('svnuuid')
502 def kwsvnuuid(repo, ctx, **args):
499 def kwsvnuuid(repo, ctx, **args):
503 """String. Converted subversion revision repository identifier."""
500 """String. Converted subversion revision repository identifier."""
504 return kwconverted(ctx, 'svnuuid')
501 return kwconverted(ctx, 'svnuuid')
505
502
506 # tell hggettext to extract docstrings from these functions:
503 # tell hggettext to extract docstrings from these functions:
507 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
504 i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
@@ -1,490 +1,480 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 # Keys in extra fields that should not be copied if the user requests.
34 # Keys in extra fields that should not be copied if the user requests.
35 bannedextrakeys = set([
35 bannedextrakeys = set([
36 # Git commit object built-ins.
36 # Git commit object built-ins.
37 'tree',
37 'tree',
38 'parent',
38 'parent',
39 'author',
39 'author',
40 'committer',
40 'committer',
41 # Mercurial built-ins.
41 # Mercurial built-ins.
42 'branch',
42 'branch',
43 'close',
43 'close',
44 ])
44 ])
45
45
46 class convert_git(common.converter_source, common.commandline):
46 class convert_git(common.converter_source, common.commandline):
47 # Windows does not support GIT_DIR= construct while other systems
47 # Windows does not support GIT_DIR= construct while other systems
48 # cannot remove environment variable. Just assume none have
48 # cannot remove environment variable. Just assume none have
49 # both issues.
49 # both issues.
50
50
51 def _gitcmd(self, cmd, *args, **kwargs):
51 def _gitcmd(self, cmd, *args, **kwargs):
52 return cmd('--git-dir=%s' % self.path, *args, **kwargs)
52 return cmd('--git-dir=%s' % self.path, *args, **kwargs)
53
53
54 def gitrun0(self, *args, **kwargs):
54 def gitrun0(self, *args, **kwargs):
55 return self._gitcmd(self.run0, *args, **kwargs)
55 return self._gitcmd(self.run0, *args, **kwargs)
56
56
57 def gitrun(self, *args, **kwargs):
57 def gitrun(self, *args, **kwargs):
58 return self._gitcmd(self.run, *args, **kwargs)
58 return self._gitcmd(self.run, *args, **kwargs)
59
59
60 def gitrunlines0(self, *args, **kwargs):
60 def gitrunlines0(self, *args, **kwargs):
61 return self._gitcmd(self.runlines0, *args, **kwargs)
61 return self._gitcmd(self.runlines0, *args, **kwargs)
62
62
63 def gitrunlines(self, *args, **kwargs):
63 def gitrunlines(self, *args, **kwargs):
64 return self._gitcmd(self.runlines, *args, **kwargs)
64 return self._gitcmd(self.runlines, *args, **kwargs)
65
65
66 def gitpipe(self, *args, **kwargs):
66 def gitpipe(self, *args, **kwargs):
67 return self._gitcmd(self._run3, *args, **kwargs)
67 return self._gitcmd(self._run3, *args, **kwargs)
68
68
69 def __init__(self, ui, path, revs=None):
69 def __init__(self, ui, path, revs=None):
70 super(convert_git, self).__init__(ui, path, revs=revs)
70 super(convert_git, self).__init__(ui, path, revs=revs)
71 common.commandline.__init__(self, ui, 'git')
71 common.commandline.__init__(self, ui, 'git')
72
72
73 # Pass an absolute path to git to prevent from ever being interpreted
73 # Pass an absolute path to git to prevent from ever being interpreted
74 # as a URL
74 # as a URL
75 path = os.path.abspath(path)
75 path = os.path.abspath(path)
76
76
77 if os.path.isdir(path + "/.git"):
77 if os.path.isdir(path + "/.git"):
78 path += "/.git"
78 path += "/.git"
79 if not os.path.exists(path + "/objects"):
79 if not os.path.exists(path + "/objects"):
80 raise common.NoRepo(_("%s does not look like a Git repository") %
80 raise common.NoRepo(_("%s does not look like a Git repository") %
81 path)
81 path)
82
82
83 # The default value (50) is based on the default for 'git diff'.
83 # The default value (50) is based on the default for 'git diff'.
84 similarity = ui.configint('convert', 'git.similarity', default=50)
84 similarity = ui.configint('convert', 'git.similarity', default=50)
85 if similarity < 0 or similarity > 100:
85 if similarity < 0 or similarity > 100:
86 raise error.Abort(_('similarity must be between 0 and 100'))
86 raise error.Abort(_('similarity must be between 0 and 100'))
87 if similarity > 0:
87 if similarity > 0:
88 self.simopt = ['-C%d%%' % similarity]
88 self.simopt = ['-C%d%%' % similarity]
89 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
89 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
90 False)
90 False)
91 if findcopiesharder:
91 if findcopiesharder:
92 self.simopt.append('--find-copies-harder')
92 self.simopt.append('--find-copies-harder')
93
93
94 renamelimit = ui.configint('convert', 'git.renamelimit',
94 renamelimit = ui.configint('convert', 'git.renamelimit',
95 default=400)
95 default=400)
96 self.simopt.append('-l%d' % renamelimit)
96 self.simopt.append('-l%d' % renamelimit)
97 else:
97 else:
98 self.simopt = []
98 self.simopt = []
99
99
100 common.checktool('git', 'git')
100 common.checktool('git', 'git')
101
101
102 self.path = path
102 self.path = path
103 self.submodules = []
103 self.submodules = []
104
104
105 self.catfilepipe = self.gitpipe('cat-file', '--batch')
105 self.catfilepipe = self.gitpipe('cat-file', '--batch')
106
106
107 self.copyextrakeys = self.ui.configlist('convert', 'git.extrakeys')
107 self.copyextrakeys = self.ui.configlist('convert', 'git.extrakeys')
108 banned = set(self.copyextrakeys) & bannedextrakeys
108 banned = set(self.copyextrakeys) & bannedextrakeys
109 if banned:
109 if banned:
110 raise error.Abort(_('copying of extra key is forbidden: %s') %
110 raise error.Abort(_('copying of extra key is forbidden: %s') %
111 _(', ').join(sorted(banned)))
111 _(', ').join(sorted(banned)))
112
112
113 committeractions = self.ui.configlist('convert', 'git.committeractions',
113 committeractions = self.ui.configlist('convert', 'git.committeractions',
114 'messagedifferent')
114 'messagedifferent')
115
115
116 messagedifferent = None
116 messagedifferent = None
117 messagealways = None
117 messagealways = None
118 for a in committeractions:
118 for a in committeractions:
119 if a.startswith(('messagedifferent', 'messagealways')):
119 if a.startswith(('messagedifferent', 'messagealways')):
120 k = a
120 k = a
121 v = None
121 v = None
122 if '=' in a:
122 if '=' in a:
123 k, v = a.split('=', 1)
123 k, v = a.split('=', 1)
124
124
125 if k == 'messagedifferent':
125 if k == 'messagedifferent':
126 messagedifferent = v or 'committer:'
126 messagedifferent = v or 'committer:'
127 elif k == 'messagealways':
127 elif k == 'messagealways':
128 messagealways = v or 'committer:'
128 messagealways = v or 'committer:'
129
129
130 if messagedifferent and messagealways:
130 if messagedifferent and messagealways:
131 raise error.Abort(_('committeractions cannot define both '
131 raise error.Abort(_('committeractions cannot define both '
132 'messagedifferent and messagealways'))
132 'messagedifferent and messagealways'))
133
133
134 dropcommitter = 'dropcommitter' in committeractions
134 dropcommitter = 'dropcommitter' in committeractions
135 replaceauthor = 'replaceauthor' in committeractions
135 replaceauthor = 'replaceauthor' in committeractions
136 replacecommitter = 'replacecommitter' in committeractions
137
136
138 if dropcommitter and (replaceauthor or replacecommitter):
137 if dropcommitter and replaceauthor:
139 raise error.Abort(_('committeractions cannot define both '
138 raise error.Abort(_('committeractions cannot define both '
140 'dropcommitter and '
139 'dropcommitter and replaceauthor'))
141 'replaceauthor/replacecommitter'))
142
140
143 if dropcommitter and messagealways:
141 if dropcommitter and messagealways:
144 raise error.Abort(_('committeractions cannot define both '
142 raise error.Abort(_('committeractions cannot define both '
145 'dropcommitter and messagealways'))
143 'dropcommitter and messagealways'))
146
144
147 if replaceauthor and replacecommitter:
148 raise error.Abort(_('committeractions cannot define both '
149 'replaceauthor and replacecommitter'))
150
151 if not messagedifferent and not messagealways:
145 if not messagedifferent and not messagealways:
152 messagedifferent = 'committer:'
146 messagedifferent = 'committer:'
153
147
154 self.committeractions = {
148 self.committeractions = {
155 'dropcommitter': dropcommitter,
149 'dropcommitter': dropcommitter,
156 'replaceauthor': replaceauthor,
150 'replaceauthor': replaceauthor,
157 'replacecommitter': replacecommitter,
158 'messagedifferent': messagedifferent,
151 'messagedifferent': messagedifferent,
159 'messagealways': messagealways,
152 'messagealways': messagealways,
160 }
153 }
161
154
162 def after(self):
155 def after(self):
163 for f in self.catfilepipe:
156 for f in self.catfilepipe:
164 f.close()
157 f.close()
165
158
166 def getheads(self):
159 def getheads(self):
167 if not self.revs:
160 if not self.revs:
168 output, status = self.gitrun('rev-parse', '--branches', '--remotes')
161 output, status = self.gitrun('rev-parse', '--branches', '--remotes')
169 heads = output.splitlines()
162 heads = output.splitlines()
170 if status:
163 if status:
171 raise error.Abort(_('cannot retrieve git heads'))
164 raise error.Abort(_('cannot retrieve git heads'))
172 else:
165 else:
173 heads = []
166 heads = []
174 for rev in self.revs:
167 for rev in self.revs:
175 rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
168 rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
176 heads.append(rawhead[:-1])
169 heads.append(rawhead[:-1])
177 if ret:
170 if ret:
178 raise error.Abort(_('cannot retrieve git head "%s"') % rev)
171 raise error.Abort(_('cannot retrieve git head "%s"') % rev)
179 return heads
172 return heads
180
173
181 def catfile(self, rev, type):
174 def catfile(self, rev, type):
182 if rev == nodemod.nullhex:
175 if rev == nodemod.nullhex:
183 raise IOError
176 raise IOError
184 self.catfilepipe[0].write(rev+'\n')
177 self.catfilepipe[0].write(rev+'\n')
185 self.catfilepipe[0].flush()
178 self.catfilepipe[0].flush()
186 info = self.catfilepipe[1].readline().split()
179 info = self.catfilepipe[1].readline().split()
187 if info[1] != type:
180 if info[1] != type:
188 raise error.Abort(_('cannot read %r object at %s') % (type, rev))
181 raise error.Abort(_('cannot read %r object at %s') % (type, rev))
189 size = int(info[2])
182 size = int(info[2])
190 data = self.catfilepipe[1].read(size)
183 data = self.catfilepipe[1].read(size)
191 if len(data) < size:
184 if len(data) < size:
192 raise error.Abort(_('cannot read %r object at %s: unexpected size')
185 raise error.Abort(_('cannot read %r object at %s: unexpected size')
193 % (type, rev))
186 % (type, rev))
194 # read the trailing newline
187 # read the trailing newline
195 self.catfilepipe[1].read(1)
188 self.catfilepipe[1].read(1)
196 return data
189 return data
197
190
198 def getfile(self, name, rev):
191 def getfile(self, name, rev):
199 if rev == nodemod.nullhex:
192 if rev == nodemod.nullhex:
200 return None, None
193 return None, None
201 if name == '.hgsub':
194 if name == '.hgsub':
202 data = '\n'.join([m.hgsub() for m in self.submoditer()])
195 data = '\n'.join([m.hgsub() for m in self.submoditer()])
203 mode = ''
196 mode = ''
204 elif name == '.hgsubstate':
197 elif name == '.hgsubstate':
205 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
198 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
206 mode = ''
199 mode = ''
207 else:
200 else:
208 data = self.catfile(rev, "blob")
201 data = self.catfile(rev, "blob")
209 mode = self.modecache[(name, rev)]
202 mode = self.modecache[(name, rev)]
210 return data, mode
203 return data, mode
211
204
212 def submoditer(self):
205 def submoditer(self):
213 null = nodemod.nullhex
206 null = nodemod.nullhex
214 for m in sorted(self.submodules, key=lambda p: p.path):
207 for m in sorted(self.submodules, key=lambda p: p.path):
215 if m.node != null:
208 if m.node != null:
216 yield m
209 yield m
217
210
218 def parsegitmodules(self, content):
211 def parsegitmodules(self, content):
219 """Parse the formatted .gitmodules file, example file format:
212 """Parse the formatted .gitmodules file, example file format:
220 [submodule "sub"]\n
213 [submodule "sub"]\n
221 \tpath = sub\n
214 \tpath = sub\n
222 \turl = git://giturl\n
215 \turl = git://giturl\n
223 """
216 """
224 self.submodules = []
217 self.submodules = []
225 c = config.config()
218 c = config.config()
226 # Each item in .gitmodules starts with whitespace that cant be parsed
219 # Each item in .gitmodules starts with whitespace that cant be parsed
227 c.parse('.gitmodules', '\n'.join(line.strip() for line in
220 c.parse('.gitmodules', '\n'.join(line.strip() for line in
228 content.split('\n')))
221 content.split('\n')))
229 for sec in c.sections():
222 for sec in c.sections():
230 s = c[sec]
223 s = c[sec]
231 if 'url' in s and 'path' in s:
224 if 'url' in s and 'path' in s:
232 self.submodules.append(submodule(s['path'], '', s['url']))
225 self.submodules.append(submodule(s['path'], '', s['url']))
233
226
234 def retrievegitmodules(self, version):
227 def retrievegitmodules(self, version):
235 modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
228 modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
236 if ret:
229 if ret:
237 # This can happen if a file is in the repo that has permissions
230 # This can happen if a file is in the repo that has permissions
238 # 160000, but there is no .gitmodules file.
231 # 160000, but there is no .gitmodules file.
239 self.ui.warn(_("warning: cannot read submodules config file in "
232 self.ui.warn(_("warning: cannot read submodules config file in "
240 "%s\n") % version)
233 "%s\n") % version)
241 return
234 return
242
235
243 try:
236 try:
244 self.parsegitmodules(modules)
237 self.parsegitmodules(modules)
245 except error.ParseError:
238 except error.ParseError:
246 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
239 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
247 % version)
240 % version)
248 return
241 return
249
242
250 for m in self.submodules:
243 for m in self.submodules:
251 node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
244 node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
252 if ret:
245 if ret:
253 continue
246 continue
254 m.node = node.strip()
247 m.node = node.strip()
255
248
256 def getchanges(self, version, full):
249 def getchanges(self, version, full):
257 if full:
250 if full:
258 raise error.Abort(_("convert from git does not support --full"))
251 raise error.Abort(_("convert from git does not support --full"))
259 self.modecache = {}
252 self.modecache = {}
260 cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
253 cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
261 output, status = self.gitrun(*cmd)
254 output, status = self.gitrun(*cmd)
262 if status:
255 if status:
263 raise error.Abort(_('cannot read changes in %s') % version)
256 raise error.Abort(_('cannot read changes in %s') % version)
264 changes = []
257 changes = []
265 copies = {}
258 copies = {}
266 seen = set()
259 seen = set()
267 entry = None
260 entry = None
268 subexists = [False]
261 subexists = [False]
269 subdeleted = [False]
262 subdeleted = [False]
270 difftree = output.split('\x00')
263 difftree = output.split('\x00')
271 lcount = len(difftree)
264 lcount = len(difftree)
272 i = 0
265 i = 0
273
266
274 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules',
267 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules',
275 False)
268 False)
276 def add(entry, f, isdest):
269 def add(entry, f, isdest):
277 seen.add(f)
270 seen.add(f)
278 h = entry[3]
271 h = entry[3]
279 p = (entry[1] == "100755")
272 p = (entry[1] == "100755")
280 s = (entry[1] == "120000")
273 s = (entry[1] == "120000")
281 renamesource = (not isdest and entry[4][0] == 'R')
274 renamesource = (not isdest and entry[4][0] == 'R')
282
275
283 if f == '.gitmodules':
276 if f == '.gitmodules':
284 if skipsubmodules:
277 if skipsubmodules:
285 return
278 return
286
279
287 subexists[0] = True
280 subexists[0] = True
288 if entry[4] == 'D' or renamesource:
281 if entry[4] == 'D' or renamesource:
289 subdeleted[0] = True
282 subdeleted[0] = True
290 changes.append(('.hgsub', nodemod.nullhex))
283 changes.append(('.hgsub', nodemod.nullhex))
291 else:
284 else:
292 changes.append(('.hgsub', ''))
285 changes.append(('.hgsub', ''))
293 elif entry[1] == '160000' or entry[0] == ':160000':
286 elif entry[1] == '160000' or entry[0] == ':160000':
294 if not skipsubmodules:
287 if not skipsubmodules:
295 subexists[0] = True
288 subexists[0] = True
296 else:
289 else:
297 if renamesource:
290 if renamesource:
298 h = nodemod.nullhex
291 h = nodemod.nullhex
299 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
292 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
300 changes.append((f, h))
293 changes.append((f, h))
301
294
302 while i < lcount:
295 while i < lcount:
303 l = difftree[i]
296 l = difftree[i]
304 i += 1
297 i += 1
305 if not entry:
298 if not entry:
306 if not l.startswith(':'):
299 if not l.startswith(':'):
307 continue
300 continue
308 entry = l.split()
301 entry = l.split()
309 continue
302 continue
310 f = l
303 f = l
311 if entry[4][0] == 'C':
304 if entry[4][0] == 'C':
312 copysrc = f
305 copysrc = f
313 copydest = difftree[i]
306 copydest = difftree[i]
314 i += 1
307 i += 1
315 f = copydest
308 f = copydest
316 copies[copydest] = copysrc
309 copies[copydest] = copysrc
317 if f not in seen:
310 if f not in seen:
318 add(entry, f, False)
311 add(entry, f, False)
319 # A file can be copied multiple times, or modified and copied
312 # A file can be copied multiple times, or modified and copied
320 # simultaneously. So f can be repeated even if fdest isn't.
313 # simultaneously. So f can be repeated even if fdest isn't.
321 if entry[4][0] == 'R':
314 if entry[4][0] == 'R':
322 # rename: next line is the destination
315 # rename: next line is the destination
323 fdest = difftree[i]
316 fdest = difftree[i]
324 i += 1
317 i += 1
325 if fdest not in seen:
318 if fdest not in seen:
326 add(entry, fdest, True)
319 add(entry, fdest, True)
327 # .gitmodules isn't imported at all, so it being copied to
320 # .gitmodules isn't imported at all, so it being copied to
328 # and fro doesn't really make sense
321 # and fro doesn't really make sense
329 if f != '.gitmodules' and fdest != '.gitmodules':
322 if f != '.gitmodules' and fdest != '.gitmodules':
330 copies[fdest] = f
323 copies[fdest] = f
331 entry = None
324 entry = None
332
325
333 if subexists[0]:
326 if subexists[0]:
334 if subdeleted[0]:
327 if subdeleted[0]:
335 changes.append(('.hgsubstate', nodemod.nullhex))
328 changes.append(('.hgsubstate', nodemod.nullhex))
336 else:
329 else:
337 self.retrievegitmodules(version)
330 self.retrievegitmodules(version)
338 changes.append(('.hgsubstate', ''))
331 changes.append(('.hgsubstate', ''))
339 return (changes, copies, set())
332 return (changes, copies, set())
340
333
341 def getcommit(self, version):
334 def getcommit(self, version):
342 c = self.catfile(version, "commit") # read the commit hash
335 c = self.catfile(version, "commit") # read the commit hash
343 end = c.find("\n\n")
336 end = c.find("\n\n")
344 message = c[end + 2:]
337 message = c[end + 2:]
345 message = self.recode(message)
338 message = self.recode(message)
346 l = c[:end].splitlines()
339 l = c[:end].splitlines()
347 parents = []
340 parents = []
348 author = committer = None
341 author = committer = None
349 extra = {}
342 extra = {}
350 for e in l[1:]:
343 for e in l[1:]:
351 n, v = e.split(" ", 1)
344 n, v = e.split(" ", 1)
352 if n == "author":
345 if n == "author":
353 p = v.split()
346 p = v.split()
354 tm, tz = p[-2:]
347 tm, tz = p[-2:]
355 author = " ".join(p[:-2])
348 author = " ".join(p[:-2])
356 if author[0] == "<": author = author[1:-1]
349 if author[0] == "<": author = author[1:-1]
357 author = self.recode(author)
350 author = self.recode(author)
358 if n == "committer":
351 if n == "committer":
359 p = v.split()
352 p = v.split()
360 tm, tz = p[-2:]
353 tm, tz = p[-2:]
361 committer = " ".join(p[:-2])
354 committer = " ".join(p[:-2])
362 if committer[0] == "<": committer = committer[1:-1]
355 if committer[0] == "<": committer = committer[1:-1]
363 committer = self.recode(committer)
356 committer = self.recode(committer)
364 if n == "parent":
357 if n == "parent":
365 parents.append(v)
358 parents.append(v)
366 if n in self.copyextrakeys:
359 if n in self.copyextrakeys:
367 extra[n] = v
360 extra[n] = v
368
361
369 if self.committeractions['dropcommitter']:
362 if self.committeractions['dropcommitter']:
370 committer = None
363 committer = None
371
372 if self.committeractions['replacecommitter']:
373 committer = author
374 elif self.committeractions['replaceauthor']:
364 elif self.committeractions['replaceauthor']:
375 author = committer
365 author = committer
376
366
377 if committer:
367 if committer:
378 messagealways = self.committeractions['messagealways']
368 messagealways = self.committeractions['messagealways']
379 messagedifferent = self.committeractions['messagedifferent']
369 messagedifferent = self.committeractions['messagedifferent']
380 if messagealways:
370 if messagealways:
381 message += '\n%s %s\n' % (messagealways, committer)
371 message += '\n%s %s\n' % (messagealways, committer)
382 elif messagedifferent and author != committer:
372 elif messagedifferent and author != committer:
383 message += '\n%s %s\n' % (messagedifferent, committer)
373 message += '\n%s %s\n' % (messagedifferent, committer)
384
374
385 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
375 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
386 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
376 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
387 date = tm + " " + str(tz)
377 date = tm + " " + str(tz)
388 saverev = self.ui.configbool('convert', 'git.saverev', True)
378 saverev = self.ui.configbool('convert', 'git.saverev', True)
389
379
390 c = common.commit(parents=parents, date=date, author=author,
380 c = common.commit(parents=parents, date=date, author=author,
391 desc=message,
381 desc=message,
392 rev=version,
382 rev=version,
393 extra=extra,
383 extra=extra,
394 saverev=saverev)
384 saverev=saverev)
395 return c
385 return c
396
386
397 def numcommits(self):
387 def numcommits(self):
398 output, ret = self.gitrunlines('rev-list', '--all')
388 output, ret = self.gitrunlines('rev-list', '--all')
399 if ret:
389 if ret:
400 raise error.Abort(_('cannot retrieve number of commits in %s') \
390 raise error.Abort(_('cannot retrieve number of commits in %s') \
401 % self.path)
391 % self.path)
402 return len(output)
392 return len(output)
403
393
404 def gettags(self):
394 def gettags(self):
405 tags = {}
395 tags = {}
406 alltags = {}
396 alltags = {}
407 output, status = self.gitrunlines('ls-remote', '--tags', self.path)
397 output, status = self.gitrunlines('ls-remote', '--tags', self.path)
408
398
409 if status:
399 if status:
410 raise error.Abort(_('cannot read tags from %s') % self.path)
400 raise error.Abort(_('cannot read tags from %s') % self.path)
411 prefix = 'refs/tags/'
401 prefix = 'refs/tags/'
412
402
413 # Build complete list of tags, both annotated and bare ones
403 # Build complete list of tags, both annotated and bare ones
414 for line in output:
404 for line in output:
415 line = line.strip()
405 line = line.strip()
416 if line.startswith("error:") or line.startswith("fatal:"):
406 if line.startswith("error:") or line.startswith("fatal:"):
417 raise error.Abort(_('cannot read tags from %s') % self.path)
407 raise error.Abort(_('cannot read tags from %s') % self.path)
418 node, tag = line.split(None, 1)
408 node, tag = line.split(None, 1)
419 if not tag.startswith(prefix):
409 if not tag.startswith(prefix):
420 continue
410 continue
421 alltags[tag[len(prefix):]] = node
411 alltags[tag[len(prefix):]] = node
422
412
423 # Filter out tag objects for annotated tag refs
413 # Filter out tag objects for annotated tag refs
424 for tag in alltags:
414 for tag in alltags:
425 if tag.endswith('^{}'):
415 if tag.endswith('^{}'):
426 tags[tag[:-3]] = alltags[tag]
416 tags[tag[:-3]] = alltags[tag]
427 else:
417 else:
428 if tag + '^{}' in alltags:
418 if tag + '^{}' in alltags:
429 continue
419 continue
430 else:
420 else:
431 tags[tag] = alltags[tag]
421 tags[tag] = alltags[tag]
432
422
433 return tags
423 return tags
434
424
435 def getchangedfiles(self, version, i):
425 def getchangedfiles(self, version, i):
436 changes = []
426 changes = []
437 if i is None:
427 if i is None:
438 output, status = self.gitrunlines('diff-tree', '--root', '-m',
428 output, status = self.gitrunlines('diff-tree', '--root', '-m',
439 '-r', version)
429 '-r', version)
440 if status:
430 if status:
441 raise error.Abort(_('cannot read changes in %s') % version)
431 raise error.Abort(_('cannot read changes in %s') % version)
442 for l in output:
432 for l in output:
443 if "\t" not in l:
433 if "\t" not in l:
444 continue
434 continue
445 m, f = l[:-1].split("\t")
435 m, f = l[:-1].split("\t")
446 changes.append(f)
436 changes.append(f)
447 else:
437 else:
448 output, status = self.gitrunlines('diff-tree', '--name-only',
438 output, status = self.gitrunlines('diff-tree', '--name-only',
449 '--root', '-r', version,
439 '--root', '-r', version,
450 '%s^%s' % (version, i + 1), '--')
440 '%s^%s' % (version, i + 1), '--')
451 if status:
441 if status:
452 raise error.Abort(_('cannot read changes in %s') % version)
442 raise error.Abort(_('cannot read changes in %s') % version)
453 changes = [f.rstrip('\n') for f in output]
443 changes = [f.rstrip('\n') for f in output]
454
444
455 return changes
445 return changes
456
446
457 def getbookmarks(self):
447 def getbookmarks(self):
458 bookmarks = {}
448 bookmarks = {}
459
449
460 # Handle local and remote branches
450 # Handle local and remote branches
461 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
451 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
462 reftypes = [
452 reftypes = [
463 # (git prefix, hg prefix)
453 # (git prefix, hg prefix)
464 ('refs/remotes/origin/', remoteprefix + '/'),
454 ('refs/remotes/origin/', remoteprefix + '/'),
465 ('refs/heads/', '')
455 ('refs/heads/', '')
466 ]
456 ]
467
457
468 exclude = set([
458 exclude = set([
469 'refs/remotes/origin/HEAD',
459 'refs/remotes/origin/HEAD',
470 ])
460 ])
471
461
472 try:
462 try:
473 output, status = self.gitrunlines('show-ref')
463 output, status = self.gitrunlines('show-ref')
474 for line in output:
464 for line in output:
475 line = line.strip()
465 line = line.strip()
476 rev, name = line.split(None, 1)
466 rev, name = line.split(None, 1)
477 # Process each type of branch
467 # Process each type of branch
478 for gitprefix, hgprefix in reftypes:
468 for gitprefix, hgprefix in reftypes:
479 if not name.startswith(gitprefix) or name in exclude:
469 if not name.startswith(gitprefix) or name in exclude:
480 continue
470 continue
481 name = '%s%s' % (hgprefix, name[len(gitprefix):])
471 name = '%s%s' % (hgprefix, name[len(gitprefix):])
482 bookmarks[name] = rev
472 bookmarks[name] = rev
483 except Exception:
473 except Exception:
484 pass
474 pass
485
475
486 return bookmarks
476 return bookmarks
487
477
488 def checkrevformat(self, revstr, mapname='splicemap'):
478 def checkrevformat(self, revstr, mapname='splicemap'):
489 """ git revision string is a 40 byte hex """
479 """ git revision string is a 40 byte hex """
490 self.checkhexformat(revstr, mapname)
480 self.checkhexformat(revstr, mapname)
@@ -1,1208 +1,1167 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
377 renamelimit config option works
378
378
379 $ cd git-repo2
379 $ cd git-repo2
380 $ cat >> copy-source << EOF
380 $ cat >> copy-source << EOF
381 > sc0
381 > sc0
382 > sc1
382 > sc1
383 > sc2
383 > sc2
384 > sc3
384 > sc3
385 > sc4
385 > sc4
386 > sc5
386 > sc5
387 > sc6
387 > sc6
388 > EOF
388 > EOF
389 $ git add copy-source
389 $ git add copy-source
390 $ commit -m 'add copy-source'
390 $ commit -m 'add copy-source'
391 $ cp copy-source source-copy0
391 $ cp copy-source source-copy0
392 $ echo 0 >> source-copy0
392 $ echo 0 >> source-copy0
393 $ cp copy-source source-copy1
393 $ cp copy-source source-copy1
394 $ echo 1 >> source-copy1
394 $ echo 1 >> source-copy1
395 $ git add source-copy0 source-copy1
395 $ git add source-copy0 source-copy1
396 $ commit -a -m 'copy copy-source 2 times'
396 $ commit -a -m 'copy copy-source 2 times'
397 $ cd ..
397 $ cd ..
398
398
399 $ hg -q convert --config convert.git.renamelimit=1 \
399 $ hg -q convert --config convert.git.renamelimit=1 \
400 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo2
400 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo2
401 $ hg -R fullrepo2 status -C --change master
401 $ hg -R fullrepo2 status -C --change master
402 A source-copy0
402 A source-copy0
403 A source-copy1
403 A source-copy1
404
404
405 $ hg -q convert --config convert.git.renamelimit=100 \
405 $ hg -q convert --config convert.git.renamelimit=100 \
406 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo3
406 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo3
407 $ hg -R fullrepo3 status -C --change master
407 $ hg -R fullrepo3 status -C --change master
408 A source-copy0
408 A source-copy0
409 copy-source
409 copy-source
410 A source-copy1
410 A source-copy1
411 copy-source
411 copy-source
412
412
413 test binary conversion (issue1359)
413 test binary conversion (issue1359)
414
414
415 $ count=19
415 $ count=19
416 $ mkdir git-repo3
416 $ mkdir git-repo3
417 $ cd git-repo3
417 $ cd git-repo3
418 $ git init-db >/dev/null 2>/dev/null
418 $ git init-db >/dev/null 2>/dev/null
419 $ $PYTHON -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
419 $ $PYTHON -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
420 $ git add b
420 $ git add b
421 $ commit -a -m addbinary
421 $ commit -a -m addbinary
422 $ cd ..
422 $ cd ..
423
423
424 convert binary file
424 convert binary file
425
425
426 $ hg convert git-repo3 git-repo3-hg
426 $ hg convert git-repo3 git-repo3-hg
427 initializing destination git-repo3-hg repository
427 initializing destination git-repo3-hg repository
428 scanning source...
428 scanning source...
429 sorting...
429 sorting...
430 converting...
430 converting...
431 0 addbinary
431 0 addbinary
432 updating bookmarks
432 updating bookmarks
433 $ cd git-repo3-hg
433 $ cd git-repo3-hg
434 $ hg up -C
434 $ hg up -C
435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 $ $PYTHON -c 'print len(file("b", "rb").read())'
436 $ $PYTHON -c 'print len(file("b", "rb").read())'
437 4096
437 4096
438 $ cd ..
438 $ cd ..
439
439
440 test author vs committer
440 test author vs committer
441
441
442 $ mkdir git-repo4
442 $ mkdir git-repo4
443 $ cd git-repo4
443 $ cd git-repo4
444 $ git init-db >/dev/null 2>/dev/null
444 $ git init-db >/dev/null 2>/dev/null
445 $ echo >> foo
445 $ echo >> foo
446 $ git add foo
446 $ git add foo
447 $ commit -a -m addfoo
447 $ commit -a -m addfoo
448 $ echo >> foo
448 $ echo >> foo
449 $ GIT_AUTHOR_NAME="nottest"
449 $ GIT_AUTHOR_NAME="nottest"
450 $ commit -a -m addfoo2
450 $ commit -a -m addfoo2
451 $ cd ..
451 $ cd ..
452
452
453 convert author committer
453 convert author committer
454
454
455 $ hg convert git-repo4 git-repo4-hg
455 $ hg convert git-repo4 git-repo4-hg
456 initializing destination git-repo4-hg repository
456 initializing destination git-repo4-hg repository
457 scanning source...
457 scanning source...
458 sorting...
458 sorting...
459 converting...
459 converting...
460 1 addfoo
460 1 addfoo
461 0 addfoo2
461 0 addfoo2
462 updating bookmarks
462 updating bookmarks
463 $ hg -R git-repo4-hg log -v
463 $ hg -R git-repo4-hg log -v
464 changeset: 1:d63e967f93da
464 changeset: 1:d63e967f93da
465 bookmark: master
465 bookmark: master
466 tag: tip
466 tag: tip
467 user: nottest <test@example.org>
467 user: nottest <test@example.org>
468 date: Mon Jan 01 00:00:21 2007 +0000
468 date: Mon Jan 01 00:00:21 2007 +0000
469 files: foo
469 files: foo
470 description:
470 description:
471 addfoo2
471 addfoo2
472
472
473 committer: test <test@example.org>
473 committer: test <test@example.org>
474
474
475
475
476 changeset: 0:0735477b0224
476 changeset: 0:0735477b0224
477 user: test <test@example.org>
477 user: test <test@example.org>
478 date: Mon Jan 01 00:00:20 2007 +0000
478 date: Mon Jan 01 00:00:20 2007 +0000
479 files: foo
479 files: foo
480 description:
480 description:
481 addfoo
481 addfoo
482
482
483
483
484
484
485 Various combinations of committeractions fail
485 Various combinations of committeractions fail
486
486
487 $ hg --config convert.git.committeractions=messagedifferent,messagealways convert git-repo4 bad-committer
487 $ hg --config convert.git.committeractions=messagedifferent,messagealways convert git-repo4 bad-committer
488 initializing destination bad-committer repository
488 initializing destination bad-committer repository
489 abort: committeractions cannot define both messagedifferent and messagealways
489 abort: committeractions cannot define both messagedifferent and messagealways
490 [255]
490 [255]
491
491
492 $ hg --config convert.git.committeractions=dropcommitter,replaceauthor convert git-repo4 bad-committer
492 $ hg --config convert.git.committeractions=dropcommitter,replaceauthor convert git-repo4 bad-committer
493 initializing destination bad-committer repository
493 initializing destination bad-committer repository
494 abort: committeractions cannot define both dropcommitter and replaceauthor/replacecommitter
494 abort: committeractions cannot define both dropcommitter and replaceauthor
495 [255]
496
497 $ hg --config convert.git.committeractions=dropcommitter,replacecommitter convert git-repo4 bad-committer
498 initializing destination bad-committer repository
499 abort: committeractions cannot define both dropcommitter and replaceauthor/replacecommitter
500 [255]
495 [255]
501
496
502 $ hg --config convert.git.committeractions=dropcommitter,messagealways convert git-repo4 bad-committer
497 $ hg --config convert.git.committeractions=dropcommitter,messagealways convert git-repo4 bad-committer
503 initializing destination bad-committer repository
498 initializing destination bad-committer repository
504 abort: committeractions cannot define both dropcommitter and messagealways
499 abort: committeractions cannot define both dropcommitter and messagealways
505 [255]
500 [255]
506
501
507 $ hg --config convert.git.committeractions=replaceauthor,replacecommitter convert git-repo4 bad-committer
508 initializing destination bad-committer repository
509 abort: committeractions cannot define both replaceauthor and replacecommitter
510 [255]
511
512 custom prefix on messagedifferent works
502 custom prefix on messagedifferent works
513
503
514 $ hg --config convert.git.committeractions=messagedifferent=different: convert git-repo4 git-repo4-hg-messagedifferentprefix
504 $ hg --config convert.git.committeractions=messagedifferent=different: convert git-repo4 git-repo4-hg-messagedifferentprefix
515 initializing destination git-repo4-hg-messagedifferentprefix repository
505 initializing destination git-repo4-hg-messagedifferentprefix repository
516 scanning source...
506 scanning source...
517 sorting...
507 sorting...
518 converting...
508 converting...
519 1 addfoo
509 1 addfoo
520 0 addfoo2
510 0 addfoo2
521 updating bookmarks
511 updating bookmarks
522
512
523 $ hg -R git-repo4-hg-messagedifferentprefix log -v
513 $ hg -R git-repo4-hg-messagedifferentprefix log -v
524 changeset: 1:2fe0c98a109d
514 changeset: 1:2fe0c98a109d
525 bookmark: master
515 bookmark: master
526 tag: tip
516 tag: tip
527 user: nottest <test@example.org>
517 user: nottest <test@example.org>
528 date: Mon Jan 01 00:00:21 2007 +0000
518 date: Mon Jan 01 00:00:21 2007 +0000
529 files: foo
519 files: foo
530 description:
520 description:
531 addfoo2
521 addfoo2
532
522
533 different: test <test@example.org>
523 different: test <test@example.org>
534
524
535
525
536 changeset: 0:0735477b0224
526 changeset: 0:0735477b0224
537 user: test <test@example.org>
527 user: test <test@example.org>
538 date: Mon Jan 01 00:00:20 2007 +0000
528 date: Mon Jan 01 00:00:20 2007 +0000
539 files: foo
529 files: foo
540 description:
530 description:
541 addfoo
531 addfoo
542
532
543
533
544
534
545 messagealways will always add the "committer: " line even if committer identical
535 messagealways will always add the "committer: " line even if committer identical
546
536
547 $ hg --config convert.git.committeractions=messagealways convert git-repo4 git-repo4-hg-messagealways
537 $ hg --config convert.git.committeractions=messagealways convert git-repo4 git-repo4-hg-messagealways
548 initializing destination git-repo4-hg-messagealways repository
538 initializing destination git-repo4-hg-messagealways repository
549 scanning source...
539 scanning source...
550 sorting...
540 sorting...
551 converting...
541 converting...
552 1 addfoo
542 1 addfoo
553 0 addfoo2
543 0 addfoo2
554 updating bookmarks
544 updating bookmarks
555
545
556 $ hg -R git-repo4-hg-messagealways log -v
546 $ hg -R git-repo4-hg-messagealways log -v
557 changeset: 1:8db057d8cd37
547 changeset: 1:8db057d8cd37
558 bookmark: master
548 bookmark: master
559 tag: tip
549 tag: tip
560 user: nottest <test@example.org>
550 user: nottest <test@example.org>
561 date: Mon Jan 01 00:00:21 2007 +0000
551 date: Mon Jan 01 00:00:21 2007 +0000
562 files: foo
552 files: foo
563 description:
553 description:
564 addfoo2
554 addfoo2
565
555
566 committer: test <test@example.org>
556 committer: test <test@example.org>
567
557
568
558
569 changeset: 0:8f71fe9c98be
559 changeset: 0:8f71fe9c98be
570 user: test <test@example.org>
560 user: test <test@example.org>
571 date: Mon Jan 01 00:00:20 2007 +0000
561 date: Mon Jan 01 00:00:20 2007 +0000
572 files: foo
562 files: foo
573 description:
563 description:
574 addfoo
564 addfoo
575
565
576 committer: test <test@example.org>
566 committer: test <test@example.org>
577
567
578
568
579
569
580 custom prefix on messagealways works
570 custom prefix on messagealways works
581
571
582 $ hg --config convert.git.committeractions=messagealways=always: convert git-repo4 git-repo4-hg-messagealwaysprefix
572 $ hg --config convert.git.committeractions=messagealways=always: convert git-repo4 git-repo4-hg-messagealwaysprefix
583 initializing destination git-repo4-hg-messagealwaysprefix repository
573 initializing destination git-repo4-hg-messagealwaysprefix repository
584 scanning source...
574 scanning source...
585 sorting...
575 sorting...
586 converting...
576 converting...
587 1 addfoo
577 1 addfoo
588 0 addfoo2
578 0 addfoo2
589 updating bookmarks
579 updating bookmarks
590
580
591 $ hg -R git-repo4-hg-messagealwaysprefix log -v
581 $ hg -R git-repo4-hg-messagealwaysprefix log -v
592 changeset: 1:83c17174de79
582 changeset: 1:83c17174de79
593 bookmark: master
583 bookmark: master
594 tag: tip
584 tag: tip
595 user: nottest <test@example.org>
585 user: nottest <test@example.org>
596 date: Mon Jan 01 00:00:21 2007 +0000
586 date: Mon Jan 01 00:00:21 2007 +0000
597 files: foo
587 files: foo
598 description:
588 description:
599 addfoo2
589 addfoo2
600
590
601 always: test <test@example.org>
591 always: test <test@example.org>
602
592
603
593
604 changeset: 0:2ac9bcb3534a
594 changeset: 0:2ac9bcb3534a
605 user: test <test@example.org>
595 user: test <test@example.org>
606 date: Mon Jan 01 00:00:20 2007 +0000
596 date: Mon Jan 01 00:00:20 2007 +0000
607 files: foo
597 files: foo
608 description:
598 description:
609 addfoo
599 addfoo
610
600
611 always: test <test@example.org>
601 always: test <test@example.org>
612
602
613
603
614
604
615 replaceauthor replaces author with committer
605 replaceauthor replaces author with committer
616
606
617 $ hg --config convert.git.committeractions=replaceauthor convert git-repo4 git-repo4-hg-replaceauthor
607 $ hg --config convert.git.committeractions=replaceauthor convert git-repo4 git-repo4-hg-replaceauthor
618 initializing destination git-repo4-hg-replaceauthor repository
608 initializing destination git-repo4-hg-replaceauthor repository
619 scanning source...
609 scanning source...
620 sorting...
610 sorting...
621 converting...
611 converting...
622 1 addfoo
612 1 addfoo
623 0 addfoo2
613 0 addfoo2
624 updating bookmarks
614 updating bookmarks
625
615
626 $ hg -R git-repo4-hg-replaceauthor log -v
616 $ hg -R git-repo4-hg-replaceauthor log -v
627 changeset: 1:122c1d8999ea
617 changeset: 1:122c1d8999ea
628 bookmark: master
618 bookmark: master
629 tag: tip
619 tag: tip
630 user: test <test@example.org>
620 user: test <test@example.org>
631 date: Mon Jan 01 00:00:21 2007 +0000
621 date: Mon Jan 01 00:00:21 2007 +0000
632 files: foo
622 files: foo
633 description:
623 description:
634 addfoo2
624 addfoo2
635
625
636
626
637 changeset: 0:0735477b0224
627 changeset: 0:0735477b0224
638 user: test <test@example.org>
628 user: test <test@example.org>
639 date: Mon Jan 01 00:00:20 2007 +0000
629 date: Mon Jan 01 00:00:20 2007 +0000
640 files: foo
630 files: foo
641 description:
631 description:
642 addfoo
632 addfoo
643
633
644
634
645
635
646 replacecommitter replaces committer with author
647
648 $ hg --config convert.git.committeractions=replacecommitter convert git-repo4 git-repo4-hg-replacecommitter
649 initializing destination git-repo4-hg-replacecommitter repository
650 scanning source...
651 sorting...
652 converting...
653 1 addfoo
654 0 addfoo2
655 updating bookmarks
656
657 $ hg -R git-repo4-hg-replacecommitter log -v
658 changeset: 1:190b2da396cc
659 bookmark: master
660 tag: tip
661 user: nottest <test@example.org>
662 date: Mon Jan 01 00:00:21 2007 +0000
663 files: foo
664 description:
665 addfoo2
666
667
668 changeset: 0:0735477b0224
669 user: test <test@example.org>
670 date: Mon Jan 01 00:00:20 2007 +0000
671 files: foo
672 description:
673 addfoo
674
675
676
677 dropcommitter removes the committer
636 dropcommitter removes the committer
678
637
679 $ hg --config convert.git.committeractions=dropcommitter convert git-repo4 git-repo4-hg-dropcommitter
638 $ hg --config convert.git.committeractions=dropcommitter convert git-repo4 git-repo4-hg-dropcommitter
680 initializing destination git-repo4-hg-dropcommitter repository
639 initializing destination git-repo4-hg-dropcommitter repository
681 scanning source...
640 scanning source...
682 sorting...
641 sorting...
683 converting...
642 converting...
684 1 addfoo
643 1 addfoo
685 0 addfoo2
644 0 addfoo2
686 updating bookmarks
645 updating bookmarks
687
646
688 $ hg -R git-repo4-hg-dropcommitter log -v
647 $ hg -R git-repo4-hg-dropcommitter log -v
689 changeset: 1:190b2da396cc
648 changeset: 1:190b2da396cc
690 bookmark: master
649 bookmark: master
691 tag: tip
650 tag: tip
692 user: nottest <test@example.org>
651 user: nottest <test@example.org>
693 date: Mon Jan 01 00:00:21 2007 +0000
652 date: Mon Jan 01 00:00:21 2007 +0000
694 files: foo
653 files: foo
695 description:
654 description:
696 addfoo2
655 addfoo2
697
656
698
657
699 changeset: 0:0735477b0224
658 changeset: 0:0735477b0224
700 user: test <test@example.org>
659 user: test <test@example.org>
701 date: Mon Jan 01 00:00:20 2007 +0000
660 date: Mon Jan 01 00:00:20 2007 +0000
702 files: foo
661 files: foo
703 description:
662 description:
704 addfoo
663 addfoo
705
664
706
665
707
666
708 --sourceorder should fail
667 --sourceorder should fail
709
668
710 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
669 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
711 initializing destination git-repo4-sourcesort-hg repository
670 initializing destination git-repo4-sourcesort-hg repository
712 abort: --sourcesort is not supported by this data source
671 abort: --sourcesort is not supported by this data source
713 [255]
672 [255]
714
673
715 test converting certain branches
674 test converting certain branches
716
675
717 $ mkdir git-testrevs
676 $ mkdir git-testrevs
718 $ cd git-testrevs
677 $ cd git-testrevs
719 $ git init
678 $ git init
720 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
679 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
721 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
680 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
722 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
681 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
723 $ git checkout -b goodbranch 'HEAD^'
682 $ git checkout -b goodbranch 'HEAD^'
724 Switched to a new branch 'goodbranch'
683 Switched to a new branch 'goodbranch'
725 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
684 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
726 $ git checkout -b badbranch 'HEAD^'
685 $ git checkout -b badbranch 'HEAD^'
727 Switched to a new branch 'badbranch'
686 Switched to a new branch 'badbranch'
728 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
687 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
729 $ cd ..
688 $ cd ..
730 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
689 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
731 initializing destination hg-testrevs repository
690 initializing destination hg-testrevs repository
732 scanning source...
691 scanning source...
733 sorting...
692 sorting...
734 converting...
693 converting...
735 2 first
694 2 first
736 1 good branch commit
695 1 good branch commit
737 0 master commit
696 0 master commit
738 updating bookmarks
697 updating bookmarks
739 $ cd hg-testrevs
698 $ cd hg-testrevs
740 $ hg log -G -T '{rev} {bookmarks}'
699 $ hg log -G -T '{rev} {bookmarks}'
741 o 2 master
700 o 2 master
742 |
701 |
743 | o 1 goodbranch
702 | o 1 goodbranch
744 |/
703 |/
745 o 0
704 o 0
746
705
747 $ cd ..
706 $ cd ..
748
707
749 test sub modules
708 test sub modules
750
709
751 $ mkdir git-repo5
710 $ mkdir git-repo5
752 $ cd git-repo5
711 $ cd git-repo5
753 $ git init-db >/dev/null 2>/dev/null
712 $ git init-db >/dev/null 2>/dev/null
754 $ echo 'sub' >> foo
713 $ echo 'sub' >> foo
755 $ git add foo
714 $ git add foo
756 $ commit -a -m 'addfoo'
715 $ commit -a -m 'addfoo'
757 $ BASE=`pwd`
716 $ BASE=`pwd`
758 $ cd ..
717 $ cd ..
759 $ mkdir git-repo6
718 $ mkdir git-repo6
760 $ cd git-repo6
719 $ cd git-repo6
761 $ git init-db >/dev/null 2>/dev/null
720 $ git init-db >/dev/null 2>/dev/null
762 $ git submodule add ${BASE} >/dev/null 2>/dev/null
721 $ git submodule add ${BASE} >/dev/null 2>/dev/null
763 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
722 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
764
723
765 test non-tab whitespace .gitmodules
724 test non-tab whitespace .gitmodules
766
725
767 $ cat >> .gitmodules <<EOF
726 $ cat >> .gitmodules <<EOF
768 > [submodule "git-repo5"]
727 > [submodule "git-repo5"]
769 > path = git-repo5
728 > path = git-repo5
770 > url = git-repo5
729 > url = git-repo5
771 > EOF
730 > EOF
772 $ git commit -q -a -m "weird white space submodule"
731 $ git commit -q -a -m "weird white space submodule"
773 $ cd ..
732 $ cd ..
774 $ hg convert git-repo6 hg-repo6
733 $ hg convert git-repo6 hg-repo6
775 initializing destination hg-repo6 repository
734 initializing destination hg-repo6 repository
776 scanning source...
735 scanning source...
777 sorting...
736 sorting...
778 converting...
737 converting...
779 1 addsubmodule
738 1 addsubmodule
780 0 weird white space submodule
739 0 weird white space submodule
781 updating bookmarks
740 updating bookmarks
782
741
783 $ rm -rf hg-repo6
742 $ rm -rf hg-repo6
784 $ cd git-repo6
743 $ cd git-repo6
785 $ git reset --hard 'HEAD^' > /dev/null
744 $ git reset --hard 'HEAD^' > /dev/null
786
745
787 test missing .gitmodules
746 test missing .gitmodules
788
747
789 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
748 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
790 $ git checkout HEAD .gitmodules
749 $ git checkout HEAD .gitmodules
791 $ git rm .gitmodules
750 $ git rm .gitmodules
792 rm '.gitmodules'
751 rm '.gitmodules'
793 $ git commit -q -m "remove .gitmodules" .gitmodules
752 $ git commit -q -m "remove .gitmodules" .gitmodules
794 $ git commit -q -m "missing .gitmodules"
753 $ git commit -q -m "missing .gitmodules"
795 $ cd ..
754 $ cd ..
796 $ hg convert git-repo6 hg-repo6 --traceback 2>&1 | grep -v "fatal: Path '.gitmodules' does not exist"
755 $ hg convert git-repo6 hg-repo6 --traceback 2>&1 | grep -v "fatal: Path '.gitmodules' does not exist"
797 initializing destination hg-repo6 repository
756 initializing destination hg-repo6 repository
798 scanning source...
757 scanning source...
799 sorting...
758 sorting...
800 converting...
759 converting...
801 2 addsubmodule
760 2 addsubmodule
802 1 remove .gitmodules
761 1 remove .gitmodules
803 0 missing .gitmodules
762 0 missing .gitmodules
804 warning: cannot read submodules config file in * (glob)
763 warning: cannot read submodules config file in * (glob)
805 updating bookmarks
764 updating bookmarks
806 $ rm -rf hg-repo6
765 $ rm -rf hg-repo6
807 $ cd git-repo6
766 $ cd git-repo6
808 $ rm -rf git-repo4
767 $ rm -rf git-repo4
809 $ git reset --hard 'HEAD^^' > /dev/null
768 $ git reset --hard 'HEAD^^' > /dev/null
810 $ cd ..
769 $ cd ..
811
770
812 test invalid splicemap1
771 test invalid splicemap1
813
772
814 $ cat > splicemap <<EOF
773 $ cat > splicemap <<EOF
815 > $VALIDID1
774 > $VALIDID1
816 > EOF
775 > EOF
817 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
776 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
818 initializing destination git-repo2-splicemap1-hg repository
777 initializing destination git-repo2-splicemap1-hg repository
819 abort: syntax error in splicemap(1): child parent1[,parent2] expected
778 abort: syntax error in splicemap(1): child parent1[,parent2] expected
820 [255]
779 [255]
821
780
822 test invalid splicemap2
781 test invalid splicemap2
823
782
824 $ cat > splicemap <<EOF
783 $ cat > splicemap <<EOF
825 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
784 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
826 > EOF
785 > EOF
827 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
786 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
828 initializing destination git-repo2-splicemap2-hg repository
787 initializing destination git-repo2-splicemap2-hg repository
829 abort: syntax error in splicemap(1): child parent1[,parent2] expected
788 abort: syntax error in splicemap(1): child parent1[,parent2] expected
830 [255]
789 [255]
831
790
832 test invalid splicemap3
791 test invalid splicemap3
833
792
834 $ cat > splicemap <<EOF
793 $ cat > splicemap <<EOF
835 > $INVALIDID1 $INVALIDID2
794 > $INVALIDID1 $INVALIDID2
836 > EOF
795 > EOF
837 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
796 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
838 initializing destination git-repo2-splicemap3-hg repository
797 initializing destination git-repo2-splicemap3-hg repository
839 abort: splicemap entry afd12345af is not a valid revision identifier
798 abort: splicemap entry afd12345af is not a valid revision identifier
840 [255]
799 [255]
841
800
842 convert sub modules
801 convert sub modules
843 $ hg convert git-repo6 git-repo6-hg
802 $ hg convert git-repo6 git-repo6-hg
844 initializing destination git-repo6-hg repository
803 initializing destination git-repo6-hg repository
845 scanning source...
804 scanning source...
846 sorting...
805 sorting...
847 converting...
806 converting...
848 0 addsubmodule
807 0 addsubmodule
849 updating bookmarks
808 updating bookmarks
850 $ hg -R git-repo6-hg log -v
809 $ hg -R git-repo6-hg log -v
851 changeset: 0:* (glob)
810 changeset: 0:* (glob)
852 bookmark: master
811 bookmark: master
853 tag: tip
812 tag: tip
854 user: nottest <test@example.org>
813 user: nottest <test@example.org>
855 date: Mon Jan 01 00:00:23 2007 +0000
814 date: Mon Jan 01 00:00:23 2007 +0000
856 files: .hgsub .hgsubstate
815 files: .hgsub .hgsubstate
857 description:
816 description:
858 addsubmodule
817 addsubmodule
859
818
860 committer: test <test@example.org>
819 committer: test <test@example.org>
861
820
862
821
863
822
864 $ cd git-repo6-hg
823 $ cd git-repo6-hg
865 $ hg up >/dev/null 2>/dev/null
824 $ hg up >/dev/null 2>/dev/null
866 $ cat .hgsubstate
825 $ cat .hgsubstate
867 * git-repo5 (glob)
826 * git-repo5 (glob)
868 $ cd git-repo5
827 $ cd git-repo5
869 $ cat foo
828 $ cat foo
870 sub
829 sub
871
830
872 $ cd ../..
831 $ cd ../..
873
832
874 make sure rename detection doesn't break removing and adding gitmodules
833 make sure rename detection doesn't break removing and adding gitmodules
875
834
876 $ cd git-repo6
835 $ cd git-repo6
877 $ git mv .gitmodules .gitmodules-renamed
836 $ git mv .gitmodules .gitmodules-renamed
878 $ commit -a -m 'rename .gitmodules'
837 $ commit -a -m 'rename .gitmodules'
879 $ git mv .gitmodules-renamed .gitmodules
838 $ git mv .gitmodules-renamed .gitmodules
880 $ commit -a -m 'rename .gitmodules back'
839 $ commit -a -m 'rename .gitmodules back'
881 $ cd ..
840 $ cd ..
882
841
883 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
842 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
884 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
843 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
885 rename .gitmodules
844 rename .gitmodules
886 $ hg -R git-repo6-hg status -C --change 'tip^'
845 $ hg -R git-repo6-hg status -C --change 'tip^'
887 A .gitmodules-renamed
846 A .gitmodules-renamed
888 R .hgsub
847 R .hgsub
889 R .hgsubstate
848 R .hgsubstate
890 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
849 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
891 rename .gitmodules back
850 rename .gitmodules back
892 $ hg -R git-repo6-hg status -C --change tip
851 $ hg -R git-repo6-hg status -C --change tip
893 A .hgsub
852 A .hgsub
894 A .hgsubstate
853 A .hgsubstate
895 R .gitmodules-renamed
854 R .gitmodules-renamed
896
855
897 convert the revision removing '.gitmodules' itself (and related
856 convert the revision removing '.gitmodules' itself (and related
898 submodules)
857 submodules)
899
858
900 $ cd git-repo6
859 $ cd git-repo6
901 $ git rm .gitmodules
860 $ git rm .gitmodules
902 rm '.gitmodules'
861 rm '.gitmodules'
903 $ git rm --cached git-repo5
862 $ git rm --cached git-repo5
904 rm 'git-repo5'
863 rm 'git-repo5'
905 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
864 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
906 $ cd ..
865 $ cd ..
907
866
908 $ hg convert -q git-repo6 git-repo6-hg
867 $ hg convert -q git-repo6 git-repo6-hg
909 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
868 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
910 remove .gitmodules and submodule git-repo5
869 remove .gitmodules and submodule git-repo5
911 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
870 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
912 .hgsub .hgsubstate
871 .hgsub .hgsubstate
913
872
914 skip submodules in the conversion
873 skip submodules in the conversion
915
874
916 $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
875 $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
917 $ hg -R no-submodules manifest --all
876 $ hg -R no-submodules manifest --all
918 .gitmodules-renamed
877 .gitmodules-renamed
919
878
920 convert using a different remote prefix
879 convert using a different remote prefix
921 $ git init git-repo7
880 $ git init git-repo7
922 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
881 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
923 $ cd git-repo7
882 $ cd git-repo7
924 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
883 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
925 git output variance, but that doesn't currently work in the middle of
884 git output variance, but that doesn't currently work in the middle of
926 a block, so do this for now.
885 a block, so do this for now.
927 $ touch a && git add a && git commit -am "commit a" | grep -v changed
886 $ touch a && git add a && git commit -am "commit a" | grep -v changed
928 [master (root-commit) 8ae5f69] commit a
887 [master (root-commit) 8ae5f69] commit a
929 Author: nottest <test@example.org>
888 Author: nottest <test@example.org>
930 create mode 100644 a
889 create mode 100644 a
931 $ cd ..
890 $ cd ..
932 $ git clone git-repo7 git-repo7-client
891 $ git clone git-repo7 git-repo7-client
933 Cloning into 'git-repo7-client'...
892 Cloning into 'git-repo7-client'...
934 done.
893 done.
935 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
894 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
936 initializing destination hg-repo7 repository
895 initializing destination hg-repo7 repository
937 scanning source...
896 scanning source...
938 sorting...
897 sorting...
939 converting...
898 converting...
940 0 commit a
899 0 commit a
941 updating bookmarks
900 updating bookmarks
942 $ hg -R hg-repo7 bookmarks
901 $ hg -R hg-repo7 bookmarks
943 master 0:03bf38caa4c6
902 master 0:03bf38caa4c6
944 origin/master 0:03bf38caa4c6
903 origin/master 0:03bf38caa4c6
945
904
946 Run convert when the remote branches have changed
905 Run convert when the remote branches have changed
947 (there was an old bug where the local convert read branches from the server)
906 (there was an old bug where the local convert read branches from the server)
948
907
949 $ cd git-repo7
908 $ cd git-repo7
950 $ echo a >> a
909 $ echo a >> a
951 $ git commit -q -am "move master forward"
910 $ git commit -q -am "move master forward"
952 $ cd ..
911 $ cd ..
953 $ rm -rf hg-repo7
912 $ rm -rf hg-repo7
954 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
913 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
955 initializing destination hg-repo7 repository
914 initializing destination hg-repo7 repository
956 scanning source...
915 scanning source...
957 sorting...
916 sorting...
958 converting...
917 converting...
959 0 commit a
918 0 commit a
960 updating bookmarks
919 updating bookmarks
961 $ hg -R hg-repo7 bookmarks
920 $ hg -R hg-repo7 bookmarks
962 master 0:03bf38caa4c6
921 master 0:03bf38caa4c6
963 origin/master 0:03bf38caa4c6
922 origin/master 0:03bf38caa4c6
964
923
965 damaged git repository tests:
924 damaged git repository tests:
966 In case the hard-coded hashes change, the following commands can be used to
925 In case the hard-coded hashes change, the following commands can be used to
967 list the hashes and their corresponding types in the repository:
926 list the hashes and their corresponding types in the repository:
968 cd git-repo4/.git/objects
927 cd git-repo4/.git/objects
969 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
928 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
970 cd ../../..
929 cd ../../..
971
930
972 damage git repository by renaming a commit object
931 damage git repository by renaming a commit object
973 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
932 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
974 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
933 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
975 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
934 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
976 abort: cannot retrieve number of commits in $TESTTMP/git-repo4/.git (glob)
935 abort: cannot retrieve number of commits in $TESTTMP/git-repo4/.git (glob)
977 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
936 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
978 damage git repository by renaming a blob object
937 damage git repository by renaming a blob object
979
938
980 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
939 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
981 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
940 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
982 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
941 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
983 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
942 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
984 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
943 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
985 damage git repository by renaming a tree object
944 damage git repository by renaming a tree object
986
945
987 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
946 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
988 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
947 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
989 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
948 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
990 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
949 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
991
950
992 #if no-windows
951 #if no-windows
993
952
994 test for escaping the repo name (CVE-2016-3069)
953 test for escaping the repo name (CVE-2016-3069)
995
954
996 $ git init '`echo pwned >COMMAND-INJECTION`'
955 $ git init '`echo pwned >COMMAND-INJECTION`'
997 Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/
956 Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/
998 $ cd '`echo pwned >COMMAND-INJECTION`'
957 $ cd '`echo pwned >COMMAND-INJECTION`'
999 $ git commit -q --allow-empty -m 'empty'
958 $ git commit -q --allow-empty -m 'empty'
1000 $ cd ..
959 $ cd ..
1001 $ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted'
960 $ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted'
1002 initializing destination converted repository
961 initializing destination converted repository
1003 scanning source...
962 scanning source...
1004 sorting...
963 sorting...
1005 converting...
964 converting...
1006 0 empty
965 0 empty
1007 updating bookmarks
966 updating bookmarks
1008 $ test -f COMMAND-INJECTION
967 $ test -f COMMAND-INJECTION
1009 [1]
968 [1]
1010
969
1011 test for safely passing paths to git (CVE-2016-3105)
970 test for safely passing paths to git (CVE-2016-3105)
1012
971
1013 $ git init 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
972 $ git init 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
1014 Initialized empty Git repository in $TESTTMP/ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #/.git/
973 Initialized empty Git repository in $TESTTMP/ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #/.git/
1015 $ cd 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
974 $ cd 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
1016 $ git commit -q --allow-empty -m 'empty'
975 $ git commit -q --allow-empty -m 'empty'
1017 $ cd ..
976 $ cd ..
1018 $ hg convert 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #' 'converted-git-ext'
977 $ hg convert 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #' 'converted-git-ext'
1019 initializing destination converted-git-ext repository
978 initializing destination converted-git-ext repository
1020 scanning source...
979 scanning source...
1021 sorting...
980 sorting...
1022 converting...
981 converting...
1023 0 empty
982 0 empty
1024 updating bookmarks
983 updating bookmarks
1025 $ test -f GIT-EXT-COMMAND-INJECTION
984 $ test -f GIT-EXT-COMMAND-INJECTION
1026 [1]
985 [1]
1027
986
1028 #endif
987 #endif
1029
988
1030 Conversion of extra commit metadata to extras works
989 Conversion of extra commit metadata to extras works
1031
990
1032 $ git init gitextras >/dev/null 2>/dev/null
991 $ git init gitextras >/dev/null 2>/dev/null
1033 $ cd gitextras
992 $ cd gitextras
1034 $ touch foo
993 $ touch foo
1035 $ git add foo
994 $ git add foo
1036 $ commit -m initial
995 $ commit -m initial
1037 $ echo 1 > foo
996 $ echo 1 > foo
1038 $ tree=`git write-tree`
997 $ tree=`git write-tree`
1039
998
1040 Git doesn't provider a user-facing API to write extra metadata into the
999 Git doesn't provider a user-facing API to write extra metadata into the
1041 commit, so create the commit object by hand
1000 commit, so create the commit object by hand
1042
1001
1043 $ git hash-object -t commit -w --stdin << EOF
1002 $ git hash-object -t commit -w --stdin << EOF
1044 > tree ${tree}
1003 > tree ${tree}
1045 > parent ba6b1344e977ece9e00958dbbf17f1f09384b2c1
1004 > parent ba6b1344e977ece9e00958dbbf17f1f09384b2c1
1046 > author test <test@example.com> 1000000000 +0000
1005 > author test <test@example.com> 1000000000 +0000
1047 > committer test <test@example.com> 1000000000 +0000
1006 > committer test <test@example.com> 1000000000 +0000
1048 > extra-1 extra-1
1007 > extra-1 extra-1
1049 > extra-2 extra-2 with space
1008 > extra-2 extra-2 with space
1050 > convert_revision 0000aaaabbbbccccddddeeee
1009 > convert_revision 0000aaaabbbbccccddddeeee
1051 >
1010 >
1052 > message with extras
1011 > message with extras
1053 > EOF
1012 > EOF
1054 8123727c8361a4117d1a2d80e0c4e7d70c757f18
1013 8123727c8361a4117d1a2d80e0c4e7d70c757f18
1055
1014
1056 $ git reset --hard 8123727c8361a4117d1a2d80e0c4e7d70c757f18 > /dev/null
1015 $ git reset --hard 8123727c8361a4117d1a2d80e0c4e7d70c757f18 > /dev/null
1057
1016
1058 $ cd ..
1017 $ cd ..
1059
1018
1060 convert will not retain custom metadata keys by default
1019 convert will not retain custom metadata keys by default
1061
1020
1062 $ hg convert gitextras hgextras1
1021 $ hg convert gitextras hgextras1
1063 initializing destination hgextras1 repository
1022 initializing destination hgextras1 repository
1064 scanning source...
1023 scanning source...
1065 sorting...
1024 sorting...
1066 converting...
1025 converting...
1067 1 initial
1026 1 initial
1068 0 message with extras
1027 0 message with extras
1069 updating bookmarks
1028 updating bookmarks
1070
1029
1071 $ hg -R hgextras1 log --debug -r 1
1030 $ hg -R hgextras1 log --debug -r 1
1072 changeset: 1:e13a39880f68479127b2a80fa0b448cc8524aa09
1031 changeset: 1:e13a39880f68479127b2a80fa0b448cc8524aa09
1073 bookmark: master
1032 bookmark: master
1074 tag: tip
1033 tag: tip
1075 phase: draft
1034 phase: draft
1076 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1035 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1077 parent: -1:0000000000000000000000000000000000000000
1036 parent: -1:0000000000000000000000000000000000000000
1078 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1037 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1079 user: test <test@example.com>
1038 user: test <test@example.com>
1080 date: Sun Sep 09 01:46:40 2001 +0000
1039 date: Sun Sep 09 01:46:40 2001 +0000
1081 extra: branch=default
1040 extra: branch=default
1082 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1041 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1083 description:
1042 description:
1084 message with extras
1043 message with extras
1085
1044
1086
1045
1087
1046
1088 Attempting to convert a banned extra is disallowed
1047 Attempting to convert a banned extra is disallowed
1089
1048
1090 $ hg convert --config convert.git.extrakeys=tree,parent gitextras hgextras-banned
1049 $ hg convert --config convert.git.extrakeys=tree,parent gitextras hgextras-banned
1091 initializing destination hgextras-banned repository
1050 initializing destination hgextras-banned repository
1092 abort: copying of extra key is forbidden: parent, tree
1051 abort: copying of extra key is forbidden: parent, tree
1093 [255]
1052 [255]
1094
1053
1095 Converting a specific extra works
1054 Converting a specific extra works
1096
1055
1097 $ hg convert --config convert.git.extrakeys=extra-1 gitextras hgextras2
1056 $ hg convert --config convert.git.extrakeys=extra-1 gitextras hgextras2
1098 initializing destination hgextras2 repository
1057 initializing destination hgextras2 repository
1099 scanning source...
1058 scanning source...
1100 sorting...
1059 sorting...
1101 converting...
1060 converting...
1102 1 initial
1061 1 initial
1103 0 message with extras
1062 0 message with extras
1104 updating bookmarks
1063 updating bookmarks
1105
1064
1106 $ hg -R hgextras2 log --debug -r 1
1065 $ hg -R hgextras2 log --debug -r 1
1107 changeset: 1:d40fb205d58597e6ecfd55b16f198be5bf436391
1066 changeset: 1:d40fb205d58597e6ecfd55b16f198be5bf436391
1108 bookmark: master
1067 bookmark: master
1109 tag: tip
1068 tag: tip
1110 phase: draft
1069 phase: draft
1111 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1070 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1112 parent: -1:0000000000000000000000000000000000000000
1071 parent: -1:0000000000000000000000000000000000000000
1113 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1072 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1114 user: test <test@example.com>
1073 user: test <test@example.com>
1115 date: Sun Sep 09 01:46:40 2001 +0000
1074 date: Sun Sep 09 01:46:40 2001 +0000
1116 extra: branch=default
1075 extra: branch=default
1117 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1076 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1118 extra: extra-1=extra-1
1077 extra: extra-1=extra-1
1119 description:
1078 description:
1120 message with extras
1079 message with extras
1121
1080
1122
1081
1123
1082
1124 Converting multiple extras works
1083 Converting multiple extras works
1125
1084
1126 $ hg convert --config convert.git.extrakeys=extra-1,extra-2 gitextras hgextras3
1085 $ hg convert --config convert.git.extrakeys=extra-1,extra-2 gitextras hgextras3
1127 initializing destination hgextras3 repository
1086 initializing destination hgextras3 repository
1128 scanning source...
1087 scanning source...
1129 sorting...
1088 sorting...
1130 converting...
1089 converting...
1131 1 initial
1090 1 initial
1132 0 message with extras
1091 0 message with extras
1133 updating bookmarks
1092 updating bookmarks
1134
1093
1135 $ hg -R hgextras3 log --debug -r 1
1094 $ hg -R hgextras3 log --debug -r 1
1136 changeset: 1:0105af33379e7b6491501fd34141b7af700fe125
1095 changeset: 1:0105af33379e7b6491501fd34141b7af700fe125
1137 bookmark: master
1096 bookmark: master
1138 tag: tip
1097 tag: tip
1139 phase: draft
1098 phase: draft
1140 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1099 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1141 parent: -1:0000000000000000000000000000000000000000
1100 parent: -1:0000000000000000000000000000000000000000
1142 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1101 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1143 user: test <test@example.com>
1102 user: test <test@example.com>
1144 date: Sun Sep 09 01:46:40 2001 +0000
1103 date: Sun Sep 09 01:46:40 2001 +0000
1145 extra: branch=default
1104 extra: branch=default
1146 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1105 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1147 extra: extra-1=extra-1
1106 extra: extra-1=extra-1
1148 extra: extra-2=extra-2 with space
1107 extra: extra-2=extra-2 with space
1149 description:
1108 description:
1150 message with extras
1109 message with extras
1151
1110
1152
1111
1153
1112
1154 convert.git.saverev can be disabled to prevent convert_revision from being written
1113 convert.git.saverev can be disabled to prevent convert_revision from being written
1155
1114
1156 $ hg convert --config convert.git.saverev=false gitextras hgextras4
1115 $ hg convert --config convert.git.saverev=false gitextras hgextras4
1157 initializing destination hgextras4 repository
1116 initializing destination hgextras4 repository
1158 scanning source...
1117 scanning source...
1159 sorting...
1118 sorting...
1160 converting...
1119 converting...
1161 1 initial
1120 1 initial
1162 0 message with extras
1121 0 message with extras
1163 updating bookmarks
1122 updating bookmarks
1164
1123
1165 $ hg -R hgextras4 log --debug -r 1
1124 $ hg -R hgextras4 log --debug -r 1
1166 changeset: 1:1dcaf4ffe5bee43fa86db2800821f6f0af212c5c
1125 changeset: 1:1dcaf4ffe5bee43fa86db2800821f6f0af212c5c
1167 bookmark: master
1126 bookmark: master
1168 tag: tip
1127 tag: tip
1169 phase: draft
1128 phase: draft
1170 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1129 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1171 parent: -1:0000000000000000000000000000000000000000
1130 parent: -1:0000000000000000000000000000000000000000
1172 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1131 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1173 user: test <test@example.com>
1132 user: test <test@example.com>
1174 date: Sun Sep 09 01:46:40 2001 +0000
1133 date: Sun Sep 09 01:46:40 2001 +0000
1175 extra: branch=default
1134 extra: branch=default
1176 description:
1135 description:
1177 message with extras
1136 message with extras
1178
1137
1179
1138
1180
1139
1181 convert.git.saverev and convert.git.extrakeys can be combined to preserve
1140 convert.git.saverev and convert.git.extrakeys can be combined to preserve
1182 convert_revision from source
1141 convert_revision from source
1183
1142
1184 $ hg convert --config convert.git.saverev=false --config convert.git.extrakeys=convert_revision gitextras hgextras5
1143 $ hg convert --config convert.git.saverev=false --config convert.git.extrakeys=convert_revision gitextras hgextras5
1185 initializing destination hgextras5 repository
1144 initializing destination hgextras5 repository
1186 scanning source...
1145 scanning source...
1187 sorting...
1146 sorting...
1188 converting...
1147 converting...
1189 1 initial
1148 1 initial
1190 0 message with extras
1149 0 message with extras
1191 updating bookmarks
1150 updating bookmarks
1192
1151
1193 $ hg -R hgextras5 log --debug -r 1
1152 $ hg -R hgextras5 log --debug -r 1
1194 changeset: 1:574d85931544d4542007664fee3747360e85ee28
1153 changeset: 1:574d85931544d4542007664fee3747360e85ee28
1195 bookmark: master
1154 bookmark: master
1196 tag: tip
1155 tag: tip
1197 phase: draft
1156 phase: draft
1198 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1157 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1199 parent: -1:0000000000000000000000000000000000000000
1158 parent: -1:0000000000000000000000000000000000000000
1200 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1159 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1201 user: test <test@example.com>
1160 user: test <test@example.com>
1202 date: Sun Sep 09 01:46:40 2001 +0000
1161 date: Sun Sep 09 01:46:40 2001 +0000
1203 extra: branch=default
1162 extra: branch=default
1204 extra: convert_revision=0000aaaabbbbccccddddeeee
1163 extra: convert_revision=0000aaaabbbbccccddddeeee
1205 description:
1164 description:
1206 message with extras
1165 message with extras
1207
1166
1208
1167
@@ -1,586 +1,583 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
264 convert.git.renamelimit
265 perform rename and copy detection up to this many changed
265 perform rename and copy detection up to this many changed
266 files in a commit. Increasing this will make rename and copy
266 files in a commit. Increasing this will make rename and copy
267 detection more accurate but will significantly slow down
267 detection more accurate but will significantly slow down
268 computation on large projects. The option is only relevant
268 computation on large projects. The option is only relevant
269 if "convert.git.similarity" is greater than 0. The default
269 if "convert.git.similarity" is greater than 0. The default
270 is "400".
270 is "400".
271 convert.git.committeractions
271 convert.git.committeractions
272 list of actions to take when processing author and committer
272 list of actions to take when processing author and committer
273 values.
273 values.
274
274
275 Git commits have separate author (who wrote the commit) and committer
275 Git commits have separate author (who wrote the commit) and committer
276 (who applied the commit) fields. Not all destinations support separate
276 (who applied the commit) fields. Not all destinations support separate
277 author and committer fields (including Mercurial). This config option
277 author and committer fields (including Mercurial). This config option
278 controls what to do with these author and committer fields during
278 controls what to do with these author and committer fields during
279 conversion.
279 conversion.
280
280
281 A value of "messagedifferent" will append a "committer: ..." line to
281 A value of "messagedifferent" will append a "committer: ..." line to
282 the commit message if the Git committer is different from the author.
282 the commit message if the Git committer is different from the author.
283 The prefix of that line can be specified using the syntax
283 The prefix of that line can be specified using the syntax
284 "messagedifferent=<prefix>". e.g. "messagedifferent=git-committer:".
284 "messagedifferent=<prefix>". e.g. "messagedifferent=git-committer:".
285 When a prefix is specified, a space will always be inserted between
285 When a prefix is specified, a space will always be inserted between
286 the prefix and the value.
286 the prefix and the value.
287
287
288 "messagealways" behaves like "messagedifferent" except it will always
288 "messagealways" behaves like "messagedifferent" except it will always
289 result in a "committer: ..." line being appended to the commit
289 result in a "committer: ..." line being appended to the commit
290 message. This value is mutually exclusive with "messagedifferent".
290 message. This value is mutually exclusive with "messagedifferent".
291
291
292 "dropcommitter" will remove references to the committer. Only
292 "dropcommitter" will remove references to the committer. Only
293 references to the author will remain. Actions that add references to
293 references to the author will remain. Actions that add references to
294 the committer will have no effect when this is set.
294 the committer will have no effect when this is set.
295
295
296 "replaceauthor" will replace the value of the author field with the
296 "replaceauthor" will replace the value of the author field with the
297 committer. Other actions that add references to the committer will
297 committer. Other actions that add references to the committer will
298 still take effect when this is set.
298 still take effect when this is set.
299
299
300 "replacecommitter" will replace the value of the committer field with
301 the author.
302
303 The default is "messagedifferent".
300 The default is "messagedifferent".
304
301
305 convert.git.extrakeys
302 convert.git.extrakeys
306 list of extra keys from commit metadata to copy to the
303 list of extra keys from commit metadata to copy to the
307 destination. Some Git repositories store extra metadata in
304 destination. Some Git repositories store extra metadata in
308 commits. By default, this non-default metadata will be lost
305 commits. By default, this non-default metadata will be lost
309 during conversion. Setting this config option can retain
306 during conversion. Setting this config option can retain
310 that metadata. Some built-in keys such as "parent" and
307 that metadata. Some built-in keys such as "parent" and
311 "branch" are not allowed to be copied.
308 "branch" are not allowed to be copied.
312 convert.git.remoteprefix
309 convert.git.remoteprefix
313 remote refs are converted as bookmarks with
310 remote refs are converted as bookmarks with
314 "convert.git.remoteprefix" as a prefix followed by a /. The
311 "convert.git.remoteprefix" as a prefix followed by a /. The
315 default is 'remote'.
312 default is 'remote'.
316 convert.git.saverev
313 convert.git.saverev
317 whether to store the original Git commit ID in the metadata
314 whether to store the original Git commit ID in the metadata
318 of the destination commit. The default is True.
315 of the destination commit. The default is True.
319 convert.git.skipsubmodules
316 convert.git.skipsubmodules
320 does not convert root level .gitmodules files or files with
317 does not convert root level .gitmodules files or files with
321 160000 mode indicating a submodule. Default is False.
318 160000 mode indicating a submodule. Default is False.
322
319
323 Perforce Source
320 Perforce Source
324 ###############
321 ###############
325
322
326 The Perforce (P4) importer can be given a p4 depot path or a client
323 The Perforce (P4) importer can be given a p4 depot path or a client
327 specification as source. It will convert all files in the source to a flat
324 specification as source. It will convert all files in the source to a flat
328 Mercurial repository, ignoring labels, branches and integrations. Note
325 Mercurial repository, ignoring labels, branches and integrations. Note
329 that when a depot path is given you then usually should specify a target
326 that when a depot path is given you then usually should specify a target
330 directory, because otherwise the target may be named "...-hg".
327 directory, because otherwise the target may be named "...-hg".
331
328
332 The following options can be set with "--config":
329 The following options can be set with "--config":
333
330
334 convert.p4.encoding
331 convert.p4.encoding
335 specify the encoding to use when decoding standard output of
332 specify the encoding to use when decoding standard output of
336 the Perforce command line tool. The default is default
333 the Perforce command line tool. The default is default
337 system encoding.
334 system encoding.
338 convert.p4.startrev
335 convert.p4.startrev
339 specify initial Perforce revision (a Perforce changelist
336 specify initial Perforce revision (a Perforce changelist
340 number).
337 number).
341
338
342 Mercurial Destination
339 Mercurial Destination
343 #####################
340 #####################
344
341
345 The Mercurial destination will recognize Mercurial subrepositories in the
342 The Mercurial destination will recognize Mercurial subrepositories in the
346 destination directory, and update the .hgsubstate file automatically if
343 destination directory, and update the .hgsubstate file automatically if
347 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
344 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
348 Converting a repository with subrepositories requires converting a single
345 Converting a repository with subrepositories requires converting a single
349 repository at a time, from the bottom up.
346 repository at a time, from the bottom up.
350
347
351 The following options are supported:
348 The following options are supported:
352
349
353 convert.hg.clonebranches
350 convert.hg.clonebranches
354 dispatch source branches in separate clones. The default is
351 dispatch source branches in separate clones. The default is
355 False.
352 False.
356 convert.hg.tagsbranch
353 convert.hg.tagsbranch
357 branch name for tag revisions, defaults to "default".
354 branch name for tag revisions, defaults to "default".
358 convert.hg.usebranchnames
355 convert.hg.usebranchnames
359 preserve branch names. The default is True.
356 preserve branch names. The default is True.
360 convert.hg.sourcename
357 convert.hg.sourcename
361 records the given string as a 'convert_source' extra value
358 records the given string as a 'convert_source' extra value
362 on each commit made in the target repository. The default is
359 on each commit made in the target repository. The default is
363 None.
360 None.
364
361
365 All Destinations
362 All Destinations
366 ################
363 ################
367
364
368 All destination types accept the following options:
365 All destination types accept the following options:
369
366
370 convert.skiptags
367 convert.skiptags
371 does not convert tags from the source repo to the target
368 does not convert tags from the source repo to the target
372 repo. The default is False.
369 repo. The default is False.
373
370
374 options ([+] can be repeated):
371 options ([+] can be repeated):
375
372
376 -s --source-type TYPE source repository type
373 -s --source-type TYPE source repository type
377 -d --dest-type TYPE destination repository type
374 -d --dest-type TYPE destination repository type
378 -r --rev REV [+] import up to source revision REV
375 -r --rev REV [+] import up to source revision REV
379 -A --authormap FILE remap usernames using this file
376 -A --authormap FILE remap usernames using this file
380 --filemap FILE remap file names using contents of file
377 --filemap FILE remap file names using contents of file
381 --full apply filemap changes by converting all files again
378 --full apply filemap changes by converting all files again
382 --splicemap FILE splice synthesized history into place
379 --splicemap FILE splice synthesized history into place
383 --branchmap FILE change branch names while converting
380 --branchmap FILE change branch names while converting
384 --branchsort try to sort changesets by branches
381 --branchsort try to sort changesets by branches
385 --datesort try to sort changesets by date
382 --datesort try to sort changesets by date
386 --sourcesort preserve source changesets order
383 --sourcesort preserve source changesets order
387 --closesort try to reorder closed revisions
384 --closesort try to reorder closed revisions
388
385
389 (some details hidden, use --verbose to show complete help)
386 (some details hidden, use --verbose to show complete help)
390 $ hg init a
387 $ hg init a
391 $ cd a
388 $ cd a
392 $ echo a > a
389 $ echo a > a
393 $ hg ci -d'0 0' -Ama
390 $ hg ci -d'0 0' -Ama
394 adding a
391 adding a
395 $ hg cp a b
392 $ hg cp a b
396 $ hg ci -d'1 0' -mb
393 $ hg ci -d'1 0' -mb
397 $ hg rm a
394 $ hg rm a
398 $ hg ci -d'2 0' -mc
395 $ hg ci -d'2 0' -mc
399 $ hg mv b a
396 $ hg mv b a
400 $ hg ci -d'3 0' -md
397 $ hg ci -d'3 0' -md
401 $ echo a >> a
398 $ echo a >> a
402 $ hg ci -d'4 0' -me
399 $ hg ci -d'4 0' -me
403 $ cd ..
400 $ cd ..
404 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
401 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
405 assuming destination a-hg
402 assuming destination a-hg
406 initializing destination a-hg repository
403 initializing destination a-hg repository
407 scanning source...
404 scanning source...
408 sorting...
405 sorting...
409 converting...
406 converting...
410 4 a
407 4 a
411 3 b
408 3 b
412 2 c
409 2 c
413 1 d
410 1 d
414 0 e
411 0 e
415 $ hg --cwd a-hg pull ../a
412 $ hg --cwd a-hg pull ../a
416 pulling from ../a
413 pulling from ../a
417 searching for changes
414 searching for changes
418 no changes found
415 no changes found
419
416
420 conversion to existing file should fail
417 conversion to existing file should fail
421
418
422 $ touch bogusfile
419 $ touch bogusfile
423 $ hg convert a bogusfile
420 $ hg convert a bogusfile
424 initializing destination bogusfile repository
421 initializing destination bogusfile repository
425 abort: cannot create new bundle repository
422 abort: cannot create new bundle repository
426 [255]
423 [255]
427
424
428 #if unix-permissions no-root
425 #if unix-permissions no-root
429
426
430 conversion to dir without permissions should fail
427 conversion to dir without permissions should fail
431
428
432 $ mkdir bogusdir
429 $ mkdir bogusdir
433 $ chmod 000 bogusdir
430 $ chmod 000 bogusdir
434
431
435 $ hg convert a bogusdir
432 $ hg convert a bogusdir
436 abort: Permission denied: 'bogusdir'
433 abort: Permission denied: 'bogusdir'
437 [255]
434 [255]
438
435
439 user permissions should succeed
436 user permissions should succeed
440
437
441 $ chmod 700 bogusdir
438 $ chmod 700 bogusdir
442 $ hg convert a bogusdir
439 $ hg convert a bogusdir
443 initializing destination bogusdir repository
440 initializing destination bogusdir repository
444 scanning source...
441 scanning source...
445 sorting...
442 sorting...
446 converting...
443 converting...
447 4 a
444 4 a
448 3 b
445 3 b
449 2 c
446 2 c
450 1 d
447 1 d
451 0 e
448 0 e
452
449
453 #endif
450 #endif
454
451
455 test pre and post conversion actions
452 test pre and post conversion actions
456
453
457 $ echo 'include b' > filemap
454 $ echo 'include b' > filemap
458 $ hg convert --debug --filemap filemap a partialb | \
455 $ hg convert --debug --filemap filemap a partialb | \
459 > grep 'run hg'
456 > grep 'run hg'
460 run hg source pre-conversion action
457 run hg source pre-conversion action
461 run hg sink pre-conversion action
458 run hg sink pre-conversion action
462 run hg sink post-conversion action
459 run hg sink post-conversion action
463 run hg source post-conversion action
460 run hg source post-conversion action
464
461
465 converting empty dir should fail "nicely
462 converting empty dir should fail "nicely
466
463
467 $ mkdir emptydir
464 $ mkdir emptydir
468
465
469 override $PATH to ensure p4 not visible; use $PYTHON in case we're
466 override $PATH to ensure p4 not visible; use $PYTHON in case we're
470 running from a devel copy, not a temp installation
467 running from a devel copy, not a temp installation
471
468
472 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
469 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
473 assuming destination emptydir-hg
470 assuming destination emptydir-hg
474 initializing destination emptydir-hg repository
471 initializing destination emptydir-hg repository
475 emptydir does not look like a CVS checkout
472 emptydir does not look like a CVS checkout
476 $TESTTMP/emptydir does not look like a Git repository (glob)
473 $TESTTMP/emptydir does not look like a Git repository (glob)
477 emptydir does not look like a Subversion repository
474 emptydir does not look like a Subversion repository
478 emptydir is not a local Mercurial repository
475 emptydir is not a local Mercurial repository
479 emptydir does not look like a darcs repository
476 emptydir does not look like a darcs repository
480 emptydir does not look like a monotone repository
477 emptydir does not look like a monotone repository
481 emptydir does not look like a GNU Arch repository
478 emptydir does not look like a GNU Arch repository
482 emptydir does not look like a Bazaar repository
479 emptydir does not look like a Bazaar repository
483 cannot find required "p4" tool
480 cannot find required "p4" tool
484 abort: emptydir: missing or unsupported repository
481 abort: emptydir: missing or unsupported repository
485 [255]
482 [255]
486
483
487 convert with imaginary source type
484 convert with imaginary source type
488
485
489 $ hg convert --source-type foo a a-foo
486 $ hg convert --source-type foo a a-foo
490 initializing destination a-foo repository
487 initializing destination a-foo repository
491 abort: foo: invalid source repository type
488 abort: foo: invalid source repository type
492 [255]
489 [255]
493
490
494 convert with imaginary sink type
491 convert with imaginary sink type
495
492
496 $ hg convert --dest-type foo a a-foo
493 $ hg convert --dest-type foo a a-foo
497 abort: foo: invalid destination repository type
494 abort: foo: invalid destination repository type
498 [255]
495 [255]
499
496
500 testing: convert must not produce duplicate entries in fncache
497 testing: convert must not produce duplicate entries in fncache
501
498
502 $ hg convert a b
499 $ hg convert a b
503 initializing destination b repository
500 initializing destination b repository
504 scanning source...
501 scanning source...
505 sorting...
502 sorting...
506 converting...
503 converting...
507 4 a
504 4 a
508 3 b
505 3 b
509 2 c
506 2 c
510 1 d
507 1 d
511 0 e
508 0 e
512
509
513 contents of fncache file:
510 contents of fncache file:
514
511
515 $ cat b/.hg/store/fncache | sort
512 $ cat b/.hg/store/fncache | sort
516 data/a.i
513 data/a.i
517 data/b.i
514 data/b.i
518
515
519 test bogus URL
516 test bogus URL
520
517
521 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
518 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
522 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
519 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
523 [255]
520 [255]
524
521
525 test revset converted() lookup
522 test revset converted() lookup
526
523
527 $ hg --config convert.hg.saverev=True convert a c
524 $ hg --config convert.hg.saverev=True convert a c
528 initializing destination c repository
525 initializing destination c repository
529 scanning source...
526 scanning source...
530 sorting...
527 sorting...
531 converting...
528 converting...
532 4 a
529 4 a
533 3 b
530 3 b
534 2 c
531 2 c
535 1 d
532 1 d
536 0 e
533 0 e
537 $ echo f > c/f
534 $ echo f > c/f
538 $ hg -R c ci -d'0 0' -Amf
535 $ hg -R c ci -d'0 0' -Amf
539 adding f
536 adding f
540 created new head
537 created new head
541 $ hg -R c log -r "converted(09d945a62ce6)"
538 $ hg -R c log -r "converted(09d945a62ce6)"
542 changeset: 1:98c3dd46a874
539 changeset: 1:98c3dd46a874
543 user: test
540 user: test
544 date: Thu Jan 01 00:00:01 1970 +0000
541 date: Thu Jan 01 00:00:01 1970 +0000
545 summary: b
542 summary: b
546
543
547 $ hg -R c log -r "converted()"
544 $ hg -R c log -r "converted()"
548 changeset: 0:31ed57b2037c
545 changeset: 0:31ed57b2037c
549 user: test
546 user: test
550 date: Thu Jan 01 00:00:00 1970 +0000
547 date: Thu Jan 01 00:00:00 1970 +0000
551 summary: a
548 summary: a
552
549
553 changeset: 1:98c3dd46a874
550 changeset: 1:98c3dd46a874
554 user: test
551 user: test
555 date: Thu Jan 01 00:00:01 1970 +0000
552 date: Thu Jan 01 00:00:01 1970 +0000
556 summary: b
553 summary: b
557
554
558 changeset: 2:3b9ca06ef716
555 changeset: 2:3b9ca06ef716
559 user: test
556 user: test
560 date: Thu Jan 01 00:00:02 1970 +0000
557 date: Thu Jan 01 00:00:02 1970 +0000
561 summary: c
558 summary: c
562
559
563 changeset: 3:4e0debd37cf2
560 changeset: 3:4e0debd37cf2
564 user: test
561 user: test
565 date: Thu Jan 01 00:00:03 1970 +0000
562 date: Thu Jan 01 00:00:03 1970 +0000
566 summary: d
563 summary: d
567
564
568 changeset: 4:9de3bc9349c5
565 changeset: 4:9de3bc9349c5
569 user: test
566 user: test
570 date: Thu Jan 01 00:00:04 1970 +0000
567 date: Thu Jan 01 00:00:04 1970 +0000
571 summary: e
568 summary: e
572
569
573
570
574 test specifying a sourcename
571 test specifying a sourcename
575 $ echo g > a/g
572 $ echo g > a/g
576 $ hg -R a ci -d'0 0' -Amg
573 $ hg -R a ci -d'0 0' -Amg
577 adding g
574 adding g
578 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
575 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
579 scanning source...
576 scanning source...
580 sorting...
577 sorting...
581 converting...
578 converting...
582 0 g
579 0 g
583 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
580 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
584 branch=default
581 branch=default
585 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
582 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
586 convert_source=mysource
583 convert_source=mysource
General Comments 0
You need to be logged in to leave comments. Login now