##// END OF EJS Templates
convert: change hg.saverev default to False...
Matt Mackall -
r7815:bcd364b2 default
parent child Browse files
Show More
@@ -1,233 +1,233 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 '''converting foreign VCS repositories to Mercurial'''
7 '''converting foreign VCS repositories to Mercurial'''
8
8
9 import convcmd
9 import convcmd
10 import cvsps
10 import cvsps
11 from mercurial import commands
11 from mercurial import commands
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 # Commands definition was moved elsewhere to ease demandload job.
14 # Commands definition was moved elsewhere to ease demandload job.
15
15
16 def convert(ui, src, dest=None, revmapfile=None, **opts):
16 def convert(ui, src, dest=None, revmapfile=None, **opts):
17 """convert a foreign SCM repository to a Mercurial one.
17 """convert a foreign SCM repository to a Mercurial one.
18
18
19 Accepted source formats [identifiers]:
19 Accepted source formats [identifiers]:
20 - Mercurial [hg]
20 - Mercurial [hg]
21 - CVS [cvs]
21 - CVS [cvs]
22 - Darcs [darcs]
22 - Darcs [darcs]
23 - git [git]
23 - git [git]
24 - Subversion [svn]
24 - Subversion [svn]
25 - Monotone [mtn]
25 - Monotone [mtn]
26 - GNU Arch [gnuarch]
26 - GNU Arch [gnuarch]
27 - Bazaar [bzr]
27 - Bazaar [bzr]
28
28
29 Accepted destination formats [identifiers]:
29 Accepted destination formats [identifiers]:
30 - Mercurial [hg]
30 - Mercurial [hg]
31 - Subversion [svn] (history on branches is not preserved)
31 - Subversion [svn] (history on branches is not preserved)
32
32
33 If no revision is given, all revisions will be converted. Otherwise,
33 If no revision is given, all revisions will be converted. Otherwise,
34 convert will only import up to the named revision (given in a format
34 convert will only import up to the named revision (given in a format
35 understood by the source).
35 understood by the source).
36
36
37 If no destination directory name is specified, it defaults to the
37 If no destination directory name is specified, it defaults to the
38 basename of the source with '-hg' appended. If the destination
38 basename of the source with '-hg' appended. If the destination
39 repository doesn't exist, it will be created.
39 repository doesn't exist, it will be created.
40
40
41 If <REVMAP> isn't given, it will be put in a default location
41 If <REVMAP> isn't given, it will be put in a default location
42 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text
42 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text
43 file that maps each source commit ID to the destination ID for
43 file that maps each source commit ID to the destination ID for
44 that revision, like so:
44 that revision, like so:
45 <source ID> <destination ID>
45 <source ID> <destination ID>
46
46
47 If the file doesn't exist, it's automatically created. It's updated
47 If the file doesn't exist, it's automatically created. It's updated
48 on each commit copied, so convert-repo can be interrupted and can
48 on each commit copied, so convert-repo can be interrupted and can
49 be run repeatedly to copy new commits.
49 be run repeatedly to copy new commits.
50
50
51 The [username mapping] file is a simple text file that maps each source
51 The [username mapping] file is a simple text file that maps each source
52 commit author to a destination commit author. It is handy for source SCMs
52 commit author to a destination commit author. It is handy for source SCMs
53 that use unix logins to identify authors (eg: CVS). One line per author
53 that use unix logins to identify authors (eg: CVS). One line per author
54 mapping and the line format is:
54 mapping and the line format is:
55 srcauthor=whatever string you want
55 srcauthor=whatever string you want
56
56
57 The filemap is a file that allows filtering and remapping of files
57 The filemap is a file that allows filtering and remapping of files
58 and directories. Comment lines start with '#'. Each line can
58 and directories. Comment lines start with '#'. Each line can
59 contain one of the following directives:
59 contain one of the following directives:
60
60
61 include path/to/file
61 include path/to/file
62
62
63 exclude path/to/file
63 exclude path/to/file
64
64
65 rename from/file to/file
65 rename from/file to/file
66
66
67 The 'include' directive causes a file, or all files under a
67 The 'include' directive causes a file, or all files under a
68 directory, to be included in the destination repository, and the
68 directory, to be included in the destination repository, and the
69 exclusion of all other files and dirs not explicitely included.
69 exclusion of all other files and dirs not explicitely included.
70 The 'exclude' directive causes files or directories to be omitted.
70 The 'exclude' directive causes files or directories to be omitted.
71 The 'rename' directive renames a file or directory. To rename from a
71 The 'rename' directive renames a file or directory. To rename from a
72 subdirectory into the root of the repository, use '.' as the path to
72 subdirectory into the root of the repository, use '.' as the path to
73 rename to.
73 rename to.
74
74
75 The splicemap is a file that allows insertion of synthetic
75 The splicemap is a file that allows insertion of synthetic
76 history, letting you specify the parents of a revision. This is
76 history, letting you specify the parents of a revision. This is
77 useful if you want to e.g. give a Subversion merge two parents, or
77 useful if you want to e.g. give a Subversion merge two parents, or
78 graft two disconnected series of history together. Each entry
78 graft two disconnected series of history together. Each entry
79 contains a key, followed by a space, followed by one or two
79 contains a key, followed by a space, followed by one or two
80 values, separated by spaces. The key is the revision ID in the
80 values, separated by spaces. The key is the revision ID in the
81 source revision control system whose parents should be modified
81 source revision control system whose parents should be modified
82 (same format as a key in .hg/shamap). The values are the revision
82 (same format as a key in .hg/shamap). The values are the revision
83 IDs (in either the source or destination revision control system)
83 IDs (in either the source or destination revision control system)
84 that should be used as the new parents for that node.
84 that should be used as the new parents for that node.
85
85
86 Mercurial Source
86 Mercurial Source
87 -----------------
87 -----------------
88
88
89 --config convert.hg.ignoreerrors=False (boolean)
89 --config convert.hg.ignoreerrors=False (boolean)
90 ignore integrity errors when reading. Use it to fix Mercurial
90 ignore integrity errors when reading. Use it to fix Mercurial
91 repositories with missing revlogs, by converting from and to
91 repositories with missing revlogs, by converting from and to
92 Mercurial.
92 Mercurial.
93 --config convert.hg.saverev=True (boolean)
93 --config convert.hg.saverev=False (boolean)
94 allow target to preserve source revision ID
94 store original revision ID in changeset (forces target IDs to change)
95 --config convert.hg.startrev=0 (hg revision identifier)
95 --config convert.hg.startrev=0 (hg revision identifier)
96 convert start revision and its descendants
96 convert start revision and its descendants
97
97
98 CVS Source
98 CVS Source
99 ----------
99 ----------
100
100
101 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
101 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
102 to indicate the starting point of what will be converted. Direct
102 to indicate the starting point of what will be converted. Direct
103 access to the repository files is not needed, unless of course
103 access to the repository files is not needed, unless of course
104 the repository is :local:. The conversion uses the top level
104 the repository is :local:. The conversion uses the top level
105 directory in the sandbox to find the CVS repository, and then uses
105 directory in the sandbox to find the CVS repository, and then uses
106 CVS rlog commands to find files to convert. This means that unless
106 CVS rlog commands to find files to convert. This means that unless
107 a filemap is given, all files under the starting directory will be
107 a filemap is given, all files under the starting directory will be
108 converted, and that any directory reorganisation in the CVS
108 converted, and that any directory reorganisation in the CVS
109 sandbox is ignored.
109 sandbox is ignored.
110
110
111 Because CVS does not have changesets, it is necessary to collect
111 Because CVS does not have changesets, it is necessary to collect
112 individual commits to CVS and merge them into changesets. CVS
112 individual commits to CVS and merge them into changesets. CVS
113 source uses its internal changeset merging code by default but can
113 source uses its internal changeset merging code by default but can
114 be configured to call the external 'cvsps' program by setting:
114 be configured to call the external 'cvsps' program by setting:
115 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
115 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
116 This is a legacy option and may be removed in future.
116 This is a legacy option and may be removed in future.
117
117
118 The options shown are the defaults.
118 The options shown are the defaults.
119
119
120 Internal cvsps is selected by setting
120 Internal cvsps is selected by setting
121 --config convert.cvsps=builtin
121 --config convert.cvsps=builtin
122 and has a few more configurable options:
122 and has a few more configurable options:
123 --config convert.cvsps.fuzz=60 (integer)
123 --config convert.cvsps.fuzz=60 (integer)
124 Specify the maximum time (in seconds) that is allowed between
124 Specify the maximum time (in seconds) that is allowed between
125 commits with identical user and log message in a single
125 commits with identical user and log message in a single
126 changeset. When very large files were checked in as part
126 changeset. When very large files were checked in as part
127 of a changeset then the default may not be long enough.
127 of a changeset then the default may not be long enough.
128 --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
128 --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
129 Specify a regular expression to which commit log messages are
129 Specify a regular expression to which commit log messages are
130 matched. If a match occurs, then the conversion process will
130 matched. If a match occurs, then the conversion process will
131 insert a dummy revision merging the branch on which this log
131 insert a dummy revision merging the branch on which this log
132 message occurs to the branch indicated in the regex.
132 message occurs to the branch indicated in the regex.
133 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
133 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
134 Specify a regular expression to which commit log messages are
134 Specify a regular expression to which commit log messages are
135 matched. If a match occurs, then the conversion process will
135 matched. If a match occurs, then the conversion process will
136 add the most recent revision on the branch indicated in the
136 add the most recent revision on the branch indicated in the
137 regex as the second parent of the changeset.
137 regex as the second parent of the changeset.
138
138
139 The hgext/convert/cvsps wrapper script allows the builtin changeset
139 The hgext/convert/cvsps wrapper script allows the builtin changeset
140 merging code to be run without doing a conversion. Its parameters and
140 merging code to be run without doing a conversion. Its parameters and
141 output are similar to that of cvsps 2.1.
141 output are similar to that of cvsps 2.1.
142
142
143 Subversion Source
143 Subversion Source
144 -----------------
144 -----------------
145
145
146 Subversion source detects classical trunk/branches/tags layouts.
146 Subversion source detects classical trunk/branches/tags layouts.
147 By default, the supplied "svn://repo/path/" source URL is
147 By default, the supplied "svn://repo/path/" source URL is
148 converted as a single branch. If "svn://repo/path/trunk" exists
148 converted as a single branch. If "svn://repo/path/trunk" exists
149 it replaces the default branch. If "svn://repo/path/branches"
149 it replaces the default branch. If "svn://repo/path/branches"
150 exists, its subdirectories are listed as possible branches. If
150 exists, its subdirectories are listed as possible branches. If
151 "svn://repo/path/tags" exists, it is looked for tags referencing
151 "svn://repo/path/tags" exists, it is looked for tags referencing
152 converted branches. Default "trunk", "branches" and "tags" values
152 converted branches. Default "trunk", "branches" and "tags" values
153 can be overriden with following options. Set them to paths
153 can be overriden with following options. Set them to paths
154 relative to the source URL, or leave them blank to disable
154 relative to the source URL, or leave them blank to disable
155 autodetection.
155 autodetection.
156
156
157 --config convert.svn.branches=branches (directory name)
157 --config convert.svn.branches=branches (directory name)
158 specify the directory containing branches
158 specify the directory containing branches
159 --config convert.svn.tags=tags (directory name)
159 --config convert.svn.tags=tags (directory name)
160 specify the directory containing tags
160 specify the directory containing tags
161 --config convert.svn.trunk=trunk (directory name)
161 --config convert.svn.trunk=trunk (directory name)
162 specify the name of the trunk branch
162 specify the name of the trunk branch
163
163
164 Source history can be retrieved starting at a specific revision,
164 Source history can be retrieved starting at a specific revision,
165 instead of being integrally converted. Only single branch
165 instead of being integrally converted. Only single branch
166 conversions are supported.
166 conversions are supported.
167
167
168 --config convert.svn.startrev=0 (svn revision number)
168 --config convert.svn.startrev=0 (svn revision number)
169 specify start Subversion revision.
169 specify start Subversion revision.
170
170
171 Mercurial Destination
171 Mercurial Destination
172 ---------------------
172 ---------------------
173
173
174 --config convert.hg.clonebranches=False (boolean)
174 --config convert.hg.clonebranches=False (boolean)
175 dispatch source branches in separate clones.
175 dispatch source branches in separate clones.
176 --config convert.hg.tagsbranch=default (branch name)
176 --config convert.hg.tagsbranch=default (branch name)
177 tag revisions branch name
177 tag revisions branch name
178 --config convert.hg.usebranchnames=True (boolean)
178 --config convert.hg.usebranchnames=True (boolean)
179 preserve branch names
179 preserve branch names
180
180
181 """
181 """
182 return convcmd.convert(ui, src, dest, revmapfile, **opts)
182 return convcmd.convert(ui, src, dest, revmapfile, **opts)
183
183
184 def debugsvnlog(ui, **opts):
184 def debugsvnlog(ui, **opts):
185 return convcmd.debugsvnlog(ui, **opts)
185 return convcmd.debugsvnlog(ui, **opts)
186
186
187 def debugcvsps(ui, *args, **opts):
187 def debugcvsps(ui, *args, **opts):
188 '''create changeset information from CVS
188 '''create changeset information from CVS
189
189
190 This command is intended as a debugging tool for the CVS to Mercurial
190 This command is intended as a debugging tool for the CVS to Mercurial
191 converter, and can be used as a direct replacement for cvsps.
191 converter, and can be used as a direct replacement for cvsps.
192
192
193 Hg debugcvsps reads the CVS rlog for current directory (or any named
193 Hg debugcvsps reads the CVS rlog for current directory (or any named
194 directory) in the CVS repository, and converts the log to a series of
194 directory) in the CVS repository, and converts the log to a series of
195 changesets based on matching commit log entries and dates.'''
195 changesets based on matching commit log entries and dates.'''
196 return cvsps.debugcvsps(ui, *args, **opts)
196 return cvsps.debugcvsps(ui, *args, **opts)
197
197
198 commands.norepo += " convert debugsvnlog debugcvsps"
198 commands.norepo += " convert debugsvnlog debugcvsps"
199
199
200 cmdtable = {
200 cmdtable = {
201 "convert":
201 "convert":
202 (convert,
202 (convert,
203 [('A', 'authors', '', _('username mapping filename')),
203 [('A', 'authors', '', _('username mapping filename')),
204 ('d', 'dest-type', '', _('destination repository type')),
204 ('d', 'dest-type', '', _('destination repository type')),
205 ('', 'filemap', '', _('remap file names using contents of file')),
205 ('', 'filemap', '', _('remap file names using contents of file')),
206 ('r', 'rev', '', _('import up to target revision REV')),
206 ('r', 'rev', '', _('import up to target revision REV')),
207 ('s', 'source-type', '', _('source repository type')),
207 ('s', 'source-type', '', _('source repository type')),
208 ('', 'splicemap', '', _('splice synthesized history into place')),
208 ('', 'splicemap', '', _('splice synthesized history into place')),
209 ('', 'datesort', None, _('try to sort changesets by date'))],
209 ('', 'datesort', None, _('try to sort changesets by date'))],
210 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
210 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
211 "debugsvnlog":
211 "debugsvnlog":
212 (debugsvnlog,
212 (debugsvnlog,
213 [],
213 [],
214 'hg debugsvnlog'),
214 'hg debugsvnlog'),
215 "debugcvsps":
215 "debugcvsps":
216 (debugcvsps,
216 (debugcvsps,
217 [
217 [
218 # Main options shared with cvsps-2.1
218 # Main options shared with cvsps-2.1
219 ('b', 'branches', [], _('only return changes on specified branches')),
219 ('b', 'branches', [], _('only return changes on specified branches')),
220 ('p', 'prefix', '', _('prefix to remove from file names')),
220 ('p', 'prefix', '', _('prefix to remove from file names')),
221 ('r', 'revisions', [], _('only return changes after or between specified tags')),
221 ('r', 'revisions', [], _('only return changes after or between specified tags')),
222 ('u', 'update-cache', None, _("update cvs log cache")),
222 ('u', 'update-cache', None, _("update cvs log cache")),
223 ('x', 'new-cache', None, _("create new cvs log cache")),
223 ('x', 'new-cache', None, _("create new cvs log cache")),
224 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
224 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
225 ('', 'root', '', _('specify cvsroot')),
225 ('', 'root', '', _('specify cvsroot')),
226 # Options specific to builtin cvsps
226 # Options specific to builtin cvsps
227 ('', 'parents', '', _('show parent changesets')),
227 ('', 'parents', '', _('show parent changesets')),
228 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
228 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
229 # Options that are ignored for compatibility with cvsps-2.1
229 # Options that are ignored for compatibility with cvsps-2.1
230 ('A', 'cvs-direct', None, _('ignored for compatibility')),
230 ('A', 'cvs-direct', None, _('ignored for compatibility')),
231 ],
231 ],
232 _('hg debugcvsps [OPTION]... [PATH]...')),
232 _('hg debugcvsps [OPTION]... [PATH]...')),
233 }
233 }
@@ -1,335 +1,335 b''
1 # hg backend for convert extension
1 # hg backend for convert extension
2
2
3 # Notes for hg->hg conversion:
3 # Notes for hg->hg conversion:
4 #
4 #
5 # * Old versions of Mercurial didn't trim the whitespace from the ends
5 # * Old versions of Mercurial didn't trim the whitespace from the ends
6 # of commit messages, but new versions do. Changesets created by
6 # of commit messages, but new versions do. Changesets created by
7 # those older versions, then converted, may thus have different
7 # those older versions, then converted, may thus have different
8 # hashes for changesets that are otherwise identical.
8 # hashes for changesets that are otherwise identical.
9 #
9 #
10 # * By default, the source revision is stored in the converted
10 # * By default, the source revision is stored in the converted
11 # revision. This will cause the converted revision to have a
11 # revision. This will cause the converted revision to have a
12 # different identity than the source. To avoid this, use the
12 # different identity than the source. To avoid this, use the
13 # following option: "--config convert.hg.saverev=false"
13 # following option: "--config convert.hg.saverev=false"
14
14
15
15
16 import os, time
16 import os, time
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18 from mercurial.node import bin, hex, nullid
18 from mercurial.node import bin, hex, nullid
19 from mercurial import hg, util, context, error
19 from mercurial import hg, util, context, error
20
20
21 from common import NoRepo, commit, converter_source, converter_sink
21 from common import NoRepo, commit, converter_source, converter_sink
22
22
23 class mercurial_sink(converter_sink):
23 class mercurial_sink(converter_sink):
24 def __init__(self, ui, path):
24 def __init__(self, ui, path):
25 converter_sink.__init__(self, ui, path)
25 converter_sink.__init__(self, ui, path)
26 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
26 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
27 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
27 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
28 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
28 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
29 self.lastbranch = None
29 self.lastbranch = None
30 if os.path.isdir(path) and len(os.listdir(path)) > 0:
30 if os.path.isdir(path) and len(os.listdir(path)) > 0:
31 try:
31 try:
32 self.repo = hg.repository(self.ui, path)
32 self.repo = hg.repository(self.ui, path)
33 if not self.repo.local():
33 if not self.repo.local():
34 raise NoRepo(_('%s is not a local Mercurial repo') % path)
34 raise NoRepo(_('%s is not a local Mercurial repo') % path)
35 except error.RepoError, err:
35 except error.RepoError, err:
36 ui.print_exc()
36 ui.print_exc()
37 raise NoRepo(err.args[0])
37 raise NoRepo(err.args[0])
38 else:
38 else:
39 try:
39 try:
40 ui.status(_('initializing destination %s repository\n') % path)
40 ui.status(_('initializing destination %s repository\n') % path)
41 self.repo = hg.repository(self.ui, path, create=True)
41 self.repo = hg.repository(self.ui, path, create=True)
42 if not self.repo.local():
42 if not self.repo.local():
43 raise NoRepo(_('%s is not a local Mercurial repo') % path)
43 raise NoRepo(_('%s is not a local Mercurial repo') % path)
44 self.created.append(path)
44 self.created.append(path)
45 except error.RepoError, err:
45 except error.RepoError, err:
46 ui.print_exc()
46 ui.print_exc()
47 raise NoRepo("could not create hg repo %s as sink" % path)
47 raise NoRepo("could not create hg repo %s as sink" % path)
48 self.lock = None
48 self.lock = None
49 self.wlock = None
49 self.wlock = None
50 self.filemapmode = False
50 self.filemapmode = False
51
51
52 def before(self):
52 def before(self):
53 self.ui.debug(_('run hg sink pre-conversion action\n'))
53 self.ui.debug(_('run hg sink pre-conversion action\n'))
54 self.wlock = self.repo.wlock()
54 self.wlock = self.repo.wlock()
55 self.lock = self.repo.lock()
55 self.lock = self.repo.lock()
56
56
57 def after(self):
57 def after(self):
58 self.ui.debug(_('run hg sink post-conversion action\n'))
58 self.ui.debug(_('run hg sink post-conversion action\n'))
59 self.lock = None
59 self.lock = None
60 self.wlock = None
60 self.wlock = None
61
61
62 def revmapfile(self):
62 def revmapfile(self):
63 return os.path.join(self.path, ".hg", "shamap")
63 return os.path.join(self.path, ".hg", "shamap")
64
64
65 def authorfile(self):
65 def authorfile(self):
66 return os.path.join(self.path, ".hg", "authormap")
66 return os.path.join(self.path, ".hg", "authormap")
67
67
68 def getheads(self):
68 def getheads(self):
69 h = self.repo.changelog.heads()
69 h = self.repo.changelog.heads()
70 return [ hex(x) for x in h ]
70 return [ hex(x) for x in h ]
71
71
72 def setbranch(self, branch, pbranches):
72 def setbranch(self, branch, pbranches):
73 if not self.clonebranches:
73 if not self.clonebranches:
74 return
74 return
75
75
76 setbranch = (branch != self.lastbranch)
76 setbranch = (branch != self.lastbranch)
77 self.lastbranch = branch
77 self.lastbranch = branch
78 if not branch:
78 if not branch:
79 branch = 'default'
79 branch = 'default'
80 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
80 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
81 pbranch = pbranches and pbranches[0][1] or 'default'
81 pbranch = pbranches and pbranches[0][1] or 'default'
82
82
83 branchpath = os.path.join(self.path, branch)
83 branchpath = os.path.join(self.path, branch)
84 if setbranch:
84 if setbranch:
85 self.after()
85 self.after()
86 try:
86 try:
87 self.repo = hg.repository(self.ui, branchpath)
87 self.repo = hg.repository(self.ui, branchpath)
88 except:
88 except:
89 self.repo = hg.repository(self.ui, branchpath, create=True)
89 self.repo = hg.repository(self.ui, branchpath, create=True)
90 self.before()
90 self.before()
91
91
92 # pbranches may bring revisions from other branches (merge parents)
92 # pbranches may bring revisions from other branches (merge parents)
93 # Make sure we have them, or pull them.
93 # Make sure we have them, or pull them.
94 missings = {}
94 missings = {}
95 for b in pbranches:
95 for b in pbranches:
96 try:
96 try:
97 self.repo.lookup(b[0])
97 self.repo.lookup(b[0])
98 except:
98 except:
99 missings.setdefault(b[1], []).append(b[0])
99 missings.setdefault(b[1], []).append(b[0])
100
100
101 if missings:
101 if missings:
102 self.after()
102 self.after()
103 for pbranch, heads in missings.iteritems():
103 for pbranch, heads in missings.iteritems():
104 pbranchpath = os.path.join(self.path, pbranch)
104 pbranchpath = os.path.join(self.path, pbranch)
105 prepo = hg.repository(self.ui, pbranchpath)
105 prepo = hg.repository(self.ui, pbranchpath)
106 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
106 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
107 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
107 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
108 self.before()
108 self.before()
109
109
110 def putcommit(self, files, copies, parents, commit, source):
110 def putcommit(self, files, copies, parents, commit, source):
111
111
112 files = dict(files)
112 files = dict(files)
113 def getfilectx(repo, memctx, f):
113 def getfilectx(repo, memctx, f):
114 v = files[f]
114 v = files[f]
115 data = source.getfile(f, v)
115 data = source.getfile(f, v)
116 e = source.getmode(f, v)
116 e = source.getmode(f, v)
117 return context.memfilectx(f, data, 'l' in e, 'x' in e, copies.get(f))
117 return context.memfilectx(f, data, 'l' in e, 'x' in e, copies.get(f))
118
118
119 pl = []
119 pl = []
120 for p in parents:
120 for p in parents:
121 if p not in pl:
121 if p not in pl:
122 pl.append(p)
122 pl.append(p)
123 parents = pl
123 parents = pl
124 nparents = len(parents)
124 nparents = len(parents)
125 if self.filemapmode and nparents == 1:
125 if self.filemapmode and nparents == 1:
126 m1node = self.repo.changelog.read(bin(parents[0]))[0]
126 m1node = self.repo.changelog.read(bin(parents[0]))[0]
127 parent = parents[0]
127 parent = parents[0]
128
128
129 if len(parents) < 2: parents.append("0" * 40)
129 if len(parents) < 2: parents.append("0" * 40)
130 if len(parents) < 2: parents.append("0" * 40)
130 if len(parents) < 2: parents.append("0" * 40)
131 p2 = parents.pop(0)
131 p2 = parents.pop(0)
132
132
133 text = commit.desc
133 text = commit.desc
134 extra = commit.extra.copy()
134 extra = commit.extra.copy()
135 if self.branchnames and commit.branch:
135 if self.branchnames and commit.branch:
136 extra['branch'] = commit.branch
136 extra['branch'] = commit.branch
137 if commit.rev:
137 if commit.rev:
138 extra['convert_revision'] = commit.rev
138 extra['convert_revision'] = commit.rev
139
139
140 while parents:
140 while parents:
141 p1 = p2
141 p1 = p2
142 p2 = parents.pop(0)
142 p2 = parents.pop(0)
143 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(), getfilectx,
143 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(), getfilectx,
144 commit.author, commit.date, extra)
144 commit.author, commit.date, extra)
145 a = self.repo.commitctx(ctx)
145 a = self.repo.commitctx(ctx)
146 text = "(octopus merge fixup)\n"
146 text = "(octopus merge fixup)\n"
147 p2 = hex(self.repo.changelog.tip())
147 p2 = hex(self.repo.changelog.tip())
148
148
149 if self.filemapmode and nparents == 1:
149 if self.filemapmode and nparents == 1:
150 man = self.repo.manifest
150 man = self.repo.manifest
151 mnode = self.repo.changelog.read(bin(p2))[0]
151 mnode = self.repo.changelog.read(bin(p2))[0]
152 if not man.cmp(m1node, man.revision(mnode)):
152 if not man.cmp(m1node, man.revision(mnode)):
153 self.repo.rollback()
153 self.repo.rollback()
154 return parent
154 return parent
155 return p2
155 return p2
156
156
157 def puttags(self, tags):
157 def puttags(self, tags):
158 try:
158 try:
159 parentctx = self.repo[self.tagsbranch]
159 parentctx = self.repo[self.tagsbranch]
160 tagparent = parentctx.node()
160 tagparent = parentctx.node()
161 except error.RepoError, inst:
161 except error.RepoError, inst:
162 parentctx = None
162 parentctx = None
163 tagparent = nullid
163 tagparent = nullid
164
164
165 try:
165 try:
166 oldlines = util.sort(parentctx['.hgtags'].data().splitlines(1))
166 oldlines = util.sort(parentctx['.hgtags'].data().splitlines(1))
167 except:
167 except:
168 oldlines = []
168 oldlines = []
169
169
170 newlines = util.sort([("%s %s\n" % (tags[tag], tag)) for tag in tags])
170 newlines = util.sort([("%s %s\n" % (tags[tag], tag)) for tag in tags])
171
171
172 if newlines == oldlines:
172 if newlines == oldlines:
173 return None
173 return None
174 data = "".join(newlines)
174 data = "".join(newlines)
175
175
176 def getfilectx(repo, memctx, f):
176 def getfilectx(repo, memctx, f):
177 return context.memfilectx(f, data, False, False, None)
177 return context.memfilectx(f, data, False, False, None)
178
178
179 self.ui.status(_("updating tags\n"))
179 self.ui.status(_("updating tags\n"))
180 date = "%s 0" % int(time.mktime(time.gmtime()))
180 date = "%s 0" % int(time.mktime(time.gmtime()))
181 extra = {'branch': self.tagsbranch}
181 extra = {'branch': self.tagsbranch}
182 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
182 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
183 [".hgtags"], getfilectx, "convert-repo", date,
183 [".hgtags"], getfilectx, "convert-repo", date,
184 extra)
184 extra)
185 self.repo.commitctx(ctx)
185 self.repo.commitctx(ctx)
186 return hex(self.repo.changelog.tip())
186 return hex(self.repo.changelog.tip())
187
187
188 def setfilemapmode(self, active):
188 def setfilemapmode(self, active):
189 self.filemapmode = active
189 self.filemapmode = active
190
190
191 class mercurial_source(converter_source):
191 class mercurial_source(converter_source):
192 def __init__(self, ui, path, rev=None):
192 def __init__(self, ui, path, rev=None):
193 converter_source.__init__(self, ui, path, rev)
193 converter_source.__init__(self, ui, path, rev)
194 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
194 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
195 self.ignored = {}
195 self.ignored = {}
196 self.saverev = ui.configbool('convert', 'hg.saverev', True)
196 self.saverev = ui.configbool('convert', 'hg.saverev', False)
197 try:
197 try:
198 self.repo = hg.repository(self.ui, path)
198 self.repo = hg.repository(self.ui, path)
199 # try to provoke an exception if this isn't really a hg
199 # try to provoke an exception if this isn't really a hg
200 # repo, but some other bogus compatible-looking url
200 # repo, but some other bogus compatible-looking url
201 if not self.repo.local():
201 if not self.repo.local():
202 raise error.RepoError()
202 raise error.RepoError()
203 except error.RepoError:
203 except error.RepoError:
204 ui.print_exc()
204 ui.print_exc()
205 raise NoRepo("%s is not a local Mercurial repo" % path)
205 raise NoRepo("%s is not a local Mercurial repo" % path)
206 self.lastrev = None
206 self.lastrev = None
207 self.lastctx = None
207 self.lastctx = None
208 self._changescache = None
208 self._changescache = None
209 self.convertfp = None
209 self.convertfp = None
210 # Restrict converted revisions to startrev descendants
210 # Restrict converted revisions to startrev descendants
211 startnode = ui.config('convert', 'hg.startrev')
211 startnode = ui.config('convert', 'hg.startrev')
212 if startnode is not None:
212 if startnode is not None:
213 try:
213 try:
214 startnode = self.repo.lookup(startnode)
214 startnode = self.repo.lookup(startnode)
215 except error.RepoError:
215 except error.RepoError:
216 raise util.Abort(_('%s is not a valid start revision')
216 raise util.Abort(_('%s is not a valid start revision')
217 % startnode)
217 % startnode)
218 startrev = self.repo.changelog.rev(startnode)
218 startrev = self.repo.changelog.rev(startnode)
219 children = {startnode: 1}
219 children = {startnode: 1}
220 for rev in self.repo.changelog.descendants(startrev):
220 for rev in self.repo.changelog.descendants(startrev):
221 children[self.repo.changelog.node(rev)] = 1
221 children[self.repo.changelog.node(rev)] = 1
222 self.keep = children.__contains__
222 self.keep = children.__contains__
223 else:
223 else:
224 self.keep = util.always
224 self.keep = util.always
225
225
226 def changectx(self, rev):
226 def changectx(self, rev):
227 if self.lastrev != rev:
227 if self.lastrev != rev:
228 self.lastctx = self.repo[rev]
228 self.lastctx = self.repo[rev]
229 self.lastrev = rev
229 self.lastrev = rev
230 return self.lastctx
230 return self.lastctx
231
231
232 def parents(self, ctx):
232 def parents(self, ctx):
233 return [p.node() for p in ctx.parents()
233 return [p.node() for p in ctx.parents()
234 if p and self.keep(p.node())]
234 if p and self.keep(p.node())]
235
235
236 def getheads(self):
236 def getheads(self):
237 if self.rev:
237 if self.rev:
238 heads = [self.repo[self.rev].node()]
238 heads = [self.repo[self.rev].node()]
239 else:
239 else:
240 heads = self.repo.heads()
240 heads = self.repo.heads()
241 return [hex(h) for h in heads if self.keep(h)]
241 return [hex(h) for h in heads if self.keep(h)]
242
242
243 def getfile(self, name, rev):
243 def getfile(self, name, rev):
244 try:
244 try:
245 return self.changectx(rev)[name].data()
245 return self.changectx(rev)[name].data()
246 except error.LookupError, err:
246 except error.LookupError, err:
247 raise IOError(err)
247 raise IOError(err)
248
248
249 def getmode(self, name, rev):
249 def getmode(self, name, rev):
250 return self.changectx(rev).manifest().flags(name)
250 return self.changectx(rev).manifest().flags(name)
251
251
252 def getchanges(self, rev):
252 def getchanges(self, rev):
253 ctx = self.changectx(rev)
253 ctx = self.changectx(rev)
254 parents = self.parents(ctx)
254 parents = self.parents(ctx)
255 if not parents:
255 if not parents:
256 files = util.sort(ctx.manifest().keys())
256 files = util.sort(ctx.manifest().keys())
257 if self.ignoreerrors:
257 if self.ignoreerrors:
258 # calling getcopies() is a simple way to detect missing
258 # calling getcopies() is a simple way to detect missing
259 # revlogs and populate self.ignored
259 # revlogs and populate self.ignored
260 self.getcopies(ctx, files)
260 self.getcopies(ctx, files)
261 return [(f, rev) for f in files if f not in self.ignored], {}
261 return [(f, rev) for f in files if f not in self.ignored], {}
262 if self._changescache and self._changescache[0] == rev:
262 if self._changescache and self._changescache[0] == rev:
263 m, a, r = self._changescache[1]
263 m, a, r = self._changescache[1]
264 else:
264 else:
265 m, a, r = self.repo.status(parents[0], ctx.node())[:3]
265 m, a, r = self.repo.status(parents[0], ctx.node())[:3]
266 # getcopies() detects missing revlogs early, run it before
266 # getcopies() detects missing revlogs early, run it before
267 # filtering the changes.
267 # filtering the changes.
268 copies = self.getcopies(ctx, m + a)
268 copies = self.getcopies(ctx, m + a)
269 changes = [(name, rev) for name in m + a + r
269 changes = [(name, rev) for name in m + a + r
270 if name not in self.ignored]
270 if name not in self.ignored]
271 return util.sort(changes), copies
271 return util.sort(changes), copies
272
272
273 def getcopies(self, ctx, files):
273 def getcopies(self, ctx, files):
274 copies = {}
274 copies = {}
275 for name in files:
275 for name in files:
276 if name in self.ignored:
276 if name in self.ignored:
277 continue
277 continue
278 try:
278 try:
279 copysource, copynode = ctx.filectx(name).renamed()
279 copysource, copynode = ctx.filectx(name).renamed()
280 if copysource in self.ignored or not self.keep(copynode):
280 if copysource in self.ignored or not self.keep(copynode):
281 continue
281 continue
282 copies[name] = copysource
282 copies[name] = copysource
283 except TypeError:
283 except TypeError:
284 pass
284 pass
285 except error.LookupError, e:
285 except error.LookupError, e:
286 if not self.ignoreerrors:
286 if not self.ignoreerrors:
287 raise
287 raise
288 self.ignored[name] = 1
288 self.ignored[name] = 1
289 self.ui.warn(_('ignoring: %s\n') % e)
289 self.ui.warn(_('ignoring: %s\n') % e)
290 return copies
290 return copies
291
291
292 def getcommit(self, rev):
292 def getcommit(self, rev):
293 ctx = self.changectx(rev)
293 ctx = self.changectx(rev)
294 parents = [hex(p) for p in self.parents(ctx)]
294 parents = [hex(p) for p in self.parents(ctx)]
295 if self.saverev:
295 if self.saverev:
296 crev = rev
296 crev = rev
297 else:
297 else:
298 crev = None
298 crev = None
299 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
299 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
300 desc=ctx.description(), rev=crev, parents=parents,
300 desc=ctx.description(), rev=crev, parents=parents,
301 branch=ctx.branch(), extra=ctx.extra())
301 branch=ctx.branch(), extra=ctx.extra())
302
302
303 def gettags(self):
303 def gettags(self):
304 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
304 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
305 return dict([(name, hex(node)) for name, node in tags
305 return dict([(name, hex(node)) for name, node in tags
306 if self.keep(node)])
306 if self.keep(node)])
307
307
308 def getchangedfiles(self, rev, i):
308 def getchangedfiles(self, rev, i):
309 ctx = self.changectx(rev)
309 ctx = self.changectx(rev)
310 parents = self.parents(ctx)
310 parents = self.parents(ctx)
311 if not parents and i is None:
311 if not parents and i is None:
312 i = 0
312 i = 0
313 changes = [], ctx.manifest().keys(), []
313 changes = [], ctx.manifest().keys(), []
314 else:
314 else:
315 i = i or 0
315 i = i or 0
316 changes = self.repo.status(parents[i], ctx.node())[:3]
316 changes = self.repo.status(parents[i], ctx.node())[:3]
317 changes = [[f for f in l if f not in self.ignored] for l in changes]
317 changes = [[f for f in l if f not in self.ignored] for l in changes]
318
318
319 if i == 0:
319 if i == 0:
320 self._changescache = (rev, changes)
320 self._changescache = (rev, changes)
321
321
322 return changes[0] + changes[1] + changes[2]
322 return changes[0] + changes[1] + changes[2]
323
323
324 def converted(self, rev, destrev):
324 def converted(self, rev, destrev):
325 if self.convertfp is None:
325 if self.convertfp is None:
326 self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
326 self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
327 'a')
327 'a')
328 self.convertfp.write('%s %s\n' % (destrev, rev))
328 self.convertfp.write('%s %s\n' % (destrev, rev))
329 self.convertfp.flush()
329 self.convertfp.flush()
330
330
331 def before(self):
331 def before(self):
332 self.ui.debug(_('run hg source pre-conversion action\n'))
332 self.ui.debug(_('run hg source pre-conversion action\n'))
333
333
334 def after(self):
334 def after(self):
335 self.ui.debug(_('run hg source post-conversion action\n'))
335 self.ui.debug(_('run hg source post-conversion action\n'))
@@ -1,88 +1,88 b''
1 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
2 merging a and e to e
2 merging a and e to e
3 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
3 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
4 (branch merge, don't forget to commit)
4 (branch merge, don't forget to commit)
5 % convert from null revision
5 % convert from null revision
6 initializing destination empty repository
6 initializing destination empty repository
7 scanning source...
7 scanning source...
8 sorting...
8 sorting...
9 converting...
9 converting...
10 % convert from zero revision
10 % convert from zero revision
11 initializing destination full repository
11 initializing destination full repository
12 scanning source...
12 scanning source...
13 sorting...
13 sorting...
14 converting...
14 converting...
15 5 0: add a b
15 5 0: add a b
16 4 1: add c
16 4 1: add c
17 3 2: copy e from a, change b
17 3 2: copy e from a, change b
18 2 3: change a
18 2 3: change a
19 1 4: merge 2 and 3, copy d from b
19 1 4: merge 2 and 3, copy d from b
20 0 5: change a
20 0 5: change a
21 o 5 "5: change a" files: a
21 o 5 "5: change a" files: a
22 |
22 |
23 o 4 "4: merge 2 and 3, copy d from b" files: d e
23 o 4 "4: merge 2 and 3, copy d from b" files: d e
24 |\
24 |\
25 | o 3 "3: change a" files: a
25 | o 3 "3: change a" files: a
26 | |
26 | |
27 o | 2 "2: copy e from a, change b" files: b e
27 o | 2 "2: copy e from a, change b" files: b e
28 | |
28 | |
29 o | 1 "1: add c" files: c
29 o | 1 "1: add c" files: c
30 |/
30 |/
31 o 0 "0: add a b" files: a b
31 o 0 "0: add a b" files: a b
32
32
33 % convert from merge parent
33 % convert from merge parent
34 initializing destination conv1 repository
34 initializing destination conv1 repository
35 scanning source...
35 scanning source...
36 sorting...
36 sorting...
37 converting...
37 converting...
38 3 1: add c
38 3 1: add c
39 2 2: copy e from a, change b
39 2 2: copy e from a, change b
40 1 4: merge 2 and 3, copy d from b
40 1 4: merge 2 and 3, copy d from b
41 0 5: change a
41 0 5: change a
42 o 3 "5: change a" files: a
42 o 3 "5: change a" files: a
43 |
43 |
44 o 2 "4: merge 2 and 3, copy d from b" files: a d e
44 o 2 "4: merge 2 and 3, copy d from b" files: a d e
45 |
45 |
46 o 1 "2: copy e from a, change b" files: b e
46 o 1 "2: copy e from a, change b" files: b e
47 |
47 |
48 o 0 "1: add c" files: a b c
48 o 0 "1: add c" files: a b c
49
49
50 % check copy preservation
50 % check copy preservation
51 changeset: 2:cb71f8e79b45
51 changeset: 2:d67b1d48a835
52 user: test
52 user: test
53 date: Thu Jan 01 00:00:04 1970 +0000
53 date: Thu Jan 01 00:00:04 1970 +0000
54 summary: 4: merge 2 and 3, copy d from b
54 summary: 4: merge 2 and 3, copy d from b
55
55
56 changeset: 1:3334790240a8
56 changeset: 1:462c431cf47d
57 user: test
57 user: test
58 date: Thu Jan 01 00:00:02 1970 +0000
58 date: Thu Jan 01 00:00:02 1970 +0000
59 summary: 2: copy e from a, change b
59 summary: 2: copy e from a, change b
60
60
61 % check copy removal on missing parent
61 % check copy removal on missing parent
62 changeset: 2:cb71f8e79b45
62 changeset: 2:d67b1d48a835
63 user: test
63 user: test
64 date: Thu Jan 01 00:00:04 1970 +0000
64 date: Thu Jan 01 00:00:04 1970 +0000
65 summary: 4: merge 2 and 3, copy d from b
65 summary: 4: merge 2 and 3, copy d from b
66
66
67 a
67 a
68 a
68 a
69 a
69 a
70 b
70 b
71 b
71 b
72 % convert from merge
72 % convert from merge
73 initializing destination conv4 repository
73 initializing destination conv4 repository
74 scanning source...
74 scanning source...
75 sorting...
75 sorting...
76 converting...
76 converting...
77 1 4: merge 2 and 3, copy d from b
77 1 4: merge 2 and 3, copy d from b
78 0 5: change a
78 0 5: change a
79 o 1 "5: change a" files: a
79 o 1 "5: change a" files: a
80 |
80 |
81 o 0 "4: merge 2 and 3, copy d from b" files: a b c d e
81 o 0 "4: merge 2 and 3, copy d from b" files: a b c d e
82
82
83 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 a
84 a
85 a
85 a
86 a
86 a
87 b
87 b
88 b
88 b
@@ -1,211 +1,211 b''
1 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
1 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
2
2
3 convert a foreign SCM repository to a Mercurial one.
3 convert a foreign SCM repository to a Mercurial one.
4
4
5 Accepted source formats [identifiers]:
5 Accepted source formats [identifiers]:
6 - Mercurial [hg]
6 - Mercurial [hg]
7 - CVS [cvs]
7 - CVS [cvs]
8 - Darcs [darcs]
8 - Darcs [darcs]
9 - git [git]
9 - git [git]
10 - Subversion [svn]
10 - Subversion [svn]
11 - Monotone [mtn]
11 - Monotone [mtn]
12 - GNU Arch [gnuarch]
12 - GNU Arch [gnuarch]
13 - Bazaar [bzr]
13 - Bazaar [bzr]
14
14
15 Accepted destination formats [identifiers]:
15 Accepted destination formats [identifiers]:
16 - Mercurial [hg]
16 - Mercurial [hg]
17 - Subversion [svn] (history on branches is not preserved)
17 - Subversion [svn] (history on branches is not preserved)
18
18
19 If no revision is given, all revisions will be converted. Otherwise,
19 If no revision is given, all revisions will be converted. Otherwise,
20 convert will only import up to the named revision (given in a format
20 convert will only import up to the named revision (given in a format
21 understood by the source).
21 understood by the source).
22
22
23 If no destination directory name is specified, it defaults to the
23 If no destination directory name is specified, it defaults to the
24 basename of the source with '-hg' appended. If the destination
24 basename of the source with '-hg' appended. If the destination
25 repository doesn't exist, it will be created.
25 repository doesn't exist, it will be created.
26
26
27 If <REVMAP> isn't given, it will be put in a default location
27 If <REVMAP> isn't given, it will be put in a default location
28 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text
28 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text
29 file that maps each source commit ID to the destination ID for
29 file that maps each source commit ID to the destination ID for
30 that revision, like so:
30 that revision, like so:
31 <source ID> <destination ID>
31 <source ID> <destination ID>
32
32
33 If the file doesn't exist, it's automatically created. It's updated
33 If the file doesn't exist, it's automatically created. It's updated
34 on each commit copied, so convert-repo can be interrupted and can
34 on each commit copied, so convert-repo can be interrupted and can
35 be run repeatedly to copy new commits.
35 be run repeatedly to copy new commits.
36
36
37 The [username mapping] file is a simple text file that maps each source
37 The [username mapping] file is a simple text file that maps each source
38 commit author to a destination commit author. It is handy for source SCMs
38 commit author to a destination commit author. It is handy for source SCMs
39 that use unix logins to identify authors (eg: CVS). One line per author
39 that use unix logins to identify authors (eg: CVS). One line per author
40 mapping and the line format is:
40 mapping and the line format is:
41 srcauthor=whatever string you want
41 srcauthor=whatever string you want
42
42
43 The filemap is a file that allows filtering and remapping of files
43 The filemap is a file that allows filtering and remapping of files
44 and directories. Comment lines start with '#'. Each line can
44 and directories. Comment lines start with '#'. Each line can
45 contain one of the following directives:
45 contain one of the following directives:
46
46
47 include path/to/file
47 include path/to/file
48
48
49 exclude path/to/file
49 exclude path/to/file
50
50
51 rename from/file to/file
51 rename from/file to/file
52
52
53 The 'include' directive causes a file, or all files under a
53 The 'include' directive causes a file, or all files under a
54 directory, to be included in the destination repository, and the
54 directory, to be included in the destination repository, and the
55 exclusion of all other files and dirs not explicitely included.
55 exclusion of all other files and dirs not explicitely included.
56 The 'exclude' directive causes files or directories to be omitted.
56 The 'exclude' directive causes files or directories to be omitted.
57 The 'rename' directive renames a file or directory. To rename from a
57 The 'rename' directive renames a file or directory. To rename from a
58 subdirectory into the root of the repository, use '.' as the path to
58 subdirectory into the root of the repository, use '.' as the path to
59 rename to.
59 rename to.
60
60
61 The splicemap is a file that allows insertion of synthetic
61 The splicemap is a file that allows insertion of synthetic
62 history, letting you specify the parents of a revision. This is
62 history, letting you specify the parents of a revision. This is
63 useful if you want to e.g. give a Subversion merge two parents, or
63 useful if you want to e.g. give a Subversion merge two parents, or
64 graft two disconnected series of history together. Each entry
64 graft two disconnected series of history together. Each entry
65 contains a key, followed by a space, followed by one or two
65 contains a key, followed by a space, followed by one or two
66 values, separated by spaces. The key is the revision ID in the
66 values, separated by spaces. The key is the revision ID in the
67 source revision control system whose parents should be modified
67 source revision control system whose parents should be modified
68 (same format as a key in .hg/shamap). The values are the revision
68 (same format as a key in .hg/shamap). The values are the revision
69 IDs (in either the source or destination revision control system)
69 IDs (in either the source or destination revision control system)
70 that should be used as the new parents for that node.
70 that should be used as the new parents for that node.
71
71
72 Mercurial Source
72 Mercurial Source
73 -----------------
73 -----------------
74
74
75 --config convert.hg.ignoreerrors=False (boolean)
75 --config convert.hg.ignoreerrors=False (boolean)
76 ignore integrity errors when reading. Use it to fix Mercurial
76 ignore integrity errors when reading. Use it to fix Mercurial
77 repositories with missing revlogs, by converting from and to
77 repositories with missing revlogs, by converting from and to
78 Mercurial.
78 Mercurial.
79 --config convert.hg.saverev=True (boolean)
79 --config convert.hg.saverev=False (boolean)
80 allow target to preserve source revision ID
80 store original revision ID in changeset (forces target IDs to change)
81 --config convert.hg.startrev=0 (hg revision identifier)
81 --config convert.hg.startrev=0 (hg revision identifier)
82 convert start revision and its descendants
82 convert start revision and its descendants
83
83
84 CVS Source
84 CVS Source
85 ----------
85 ----------
86
86
87 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
87 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
88 to indicate the starting point of what will be converted. Direct
88 to indicate the starting point of what will be converted. Direct
89 access to the repository files is not needed, unless of course
89 access to the repository files is not needed, unless of course
90 the repository is :local:. The conversion uses the top level
90 the repository is :local:. The conversion uses the top level
91 directory in the sandbox to find the CVS repository, and then uses
91 directory in the sandbox to find the CVS repository, and then uses
92 CVS rlog commands to find files to convert. This means that unless
92 CVS rlog commands to find files to convert. This means that unless
93 a filemap is given, all files under the starting directory will be
93 a filemap is given, all files under the starting directory will be
94 converted, and that any directory reorganisation in the CVS
94 converted, and that any directory reorganisation in the CVS
95 sandbox is ignored.
95 sandbox is ignored.
96
96
97 Because CVS does not have changesets, it is necessary to collect
97 Because CVS does not have changesets, it is necessary to collect
98 individual commits to CVS and merge them into changesets. CVS
98 individual commits to CVS and merge them into changesets. CVS
99 source uses its internal changeset merging code by default but can
99 source uses its internal changeset merging code by default but can
100 be configured to call the external 'cvsps' program by setting:
100 be configured to call the external 'cvsps' program by setting:
101 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
101 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
102 This is a legacy option and may be removed in future.
102 This is a legacy option and may be removed in future.
103
103
104 The options shown are the defaults.
104 The options shown are the defaults.
105
105
106 Internal cvsps is selected by setting
106 Internal cvsps is selected by setting
107 --config convert.cvsps=builtin
107 --config convert.cvsps=builtin
108 and has a few more configurable options:
108 and has a few more configurable options:
109 --config convert.cvsps.fuzz=60 (integer)
109 --config convert.cvsps.fuzz=60 (integer)
110 Specify the maximum time (in seconds) that is allowed between
110 Specify the maximum time (in seconds) that is allowed between
111 commits with identical user and log message in a single
111 commits with identical user and log message in a single
112 changeset. When very large files were checked in as part
112 changeset. When very large files were checked in as part
113 of a changeset then the default may not be long enough.
113 of a changeset then the default may not be long enough.
114 --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
114 --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
115 Specify a regular expression to which commit log messages are
115 Specify a regular expression to which commit log messages are
116 matched. If a match occurs, then the conversion process will
116 matched. If a match occurs, then the conversion process will
117 insert a dummy revision merging the branch on which this log
117 insert a dummy revision merging the branch on which this log
118 message occurs to the branch indicated in the regex.
118 message occurs to the branch indicated in the regex.
119 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
119 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
120 Specify a regular expression to which commit log messages are
120 Specify a regular expression to which commit log messages are
121 matched. If a match occurs, then the conversion process will
121 matched. If a match occurs, then the conversion process will
122 add the most recent revision on the branch indicated in the
122 add the most recent revision on the branch indicated in the
123 regex as the second parent of the changeset.
123 regex as the second parent of the changeset.
124
124
125 The hgext/convert/cvsps wrapper script allows the builtin changeset
125 The hgext/convert/cvsps wrapper script allows the builtin changeset
126 merging code to be run without doing a conversion. Its parameters and
126 merging code to be run without doing a conversion. Its parameters and
127 output are similar to that of cvsps 2.1.
127 output are similar to that of cvsps 2.1.
128
128
129 Subversion Source
129 Subversion Source
130 -----------------
130 -----------------
131
131
132 Subversion source detects classical trunk/branches/tags layouts.
132 Subversion source detects classical trunk/branches/tags layouts.
133 By default, the supplied "svn://repo/path/" source URL is
133 By default, the supplied "svn://repo/path/" source URL is
134 converted as a single branch. If "svn://repo/path/trunk" exists
134 converted as a single branch. If "svn://repo/path/trunk" exists
135 it replaces the default branch. If "svn://repo/path/branches"
135 it replaces the default branch. If "svn://repo/path/branches"
136 exists, its subdirectories are listed as possible branches. If
136 exists, its subdirectories are listed as possible branches. If
137 "svn://repo/path/tags" exists, it is looked for tags referencing
137 "svn://repo/path/tags" exists, it is looked for tags referencing
138 converted branches. Default "trunk", "branches" and "tags" values
138 converted branches. Default "trunk", "branches" and "tags" values
139 can be overriden with following options. Set them to paths
139 can be overriden with following options. Set them to paths
140 relative to the source URL, or leave them blank to disable
140 relative to the source URL, or leave them blank to disable
141 autodetection.
141 autodetection.
142
142
143 --config convert.svn.branches=branches (directory name)
143 --config convert.svn.branches=branches (directory name)
144 specify the directory containing branches
144 specify the directory containing branches
145 --config convert.svn.tags=tags (directory name)
145 --config convert.svn.tags=tags (directory name)
146 specify the directory containing tags
146 specify the directory containing tags
147 --config convert.svn.trunk=trunk (directory name)
147 --config convert.svn.trunk=trunk (directory name)
148 specify the name of the trunk branch
148 specify the name of the trunk branch
149
149
150 Source history can be retrieved starting at a specific revision,
150 Source history can be retrieved starting at a specific revision,
151 instead of being integrally converted. Only single branch
151 instead of being integrally converted. Only single branch
152 conversions are supported.
152 conversions are supported.
153
153
154 --config convert.svn.startrev=0 (svn revision number)
154 --config convert.svn.startrev=0 (svn revision number)
155 specify start Subversion revision.
155 specify start Subversion revision.
156
156
157 Mercurial Destination
157 Mercurial Destination
158 ---------------------
158 ---------------------
159
159
160 --config convert.hg.clonebranches=False (boolean)
160 --config convert.hg.clonebranches=False (boolean)
161 dispatch source branches in separate clones.
161 dispatch source branches in separate clones.
162 --config convert.hg.tagsbranch=default (branch name)
162 --config convert.hg.tagsbranch=default (branch name)
163 tag revisions branch name
163 tag revisions branch name
164 --config convert.hg.usebranchnames=True (boolean)
164 --config convert.hg.usebranchnames=True (boolean)
165 preserve branch names
165 preserve branch names
166
166
167 options:
167 options:
168
168
169 -A --authors username mapping filename
169 -A --authors username mapping filename
170 -d --dest-type destination repository type
170 -d --dest-type destination repository type
171 --filemap remap file names using contents of file
171 --filemap remap file names using contents of file
172 -r --rev import up to target revision REV
172 -r --rev import up to target revision REV
173 -s --source-type source repository type
173 -s --source-type source repository type
174 --splicemap splice synthesized history into place
174 --splicemap splice synthesized history into place
175 --datesort try to sort changesets by date
175 --datesort try to sort changesets by date
176
176
177 use "hg -v help convert" to show global options
177 use "hg -v help convert" to show global options
178 adding a
178 adding a
179 assuming destination a-hg
179 assuming destination a-hg
180 initializing destination a-hg repository
180 initializing destination a-hg repository
181 scanning source...
181 scanning source...
182 sorting...
182 sorting...
183 converting...
183 converting...
184 4 a
184 4 a
185 3 b
185 3 b
186 2 c
186 2 c
187 1 d
187 1 d
188 0 e
188 0 e
189 pulling from ../a
189 pulling from ../a
190 searching for changes
190 searching for changes
191 no changes found
191 no changes found
192 % should fail
192 % should fail
193 initializing destination bogusfile repository
193 initializing destination bogusfile repository
194 abort: cannot create new bundle repository
194 abort: cannot create new bundle repository
195 % should fail
195 % should fail
196 abort: Permission denied: bogusdir
196 abort: Permission denied: bogusdir
197 % should succeed
197 % should succeed
198 initializing destination bogusdir repository
198 initializing destination bogusdir repository
199 scanning source...
199 scanning source...
200 sorting...
200 sorting...
201 converting...
201 converting...
202 4 a
202 4 a
203 3 b
203 3 b
204 2 c
204 2 c
205 1 d
205 1 d
206 0 e
206 0 e
207 % test pre and post conversion actions
207 % test pre and post conversion actions
208 run hg source pre-conversion action
208 run hg source pre-conversion action
209 run hg sink pre-conversion action
209 run hg sink pre-conversion action
210 run hg sink post-conversion action
210 run hg sink post-conversion action
211 run hg source post-conversion action
211 run hg source post-conversion action
General Comments 0
You need to be logged in to leave comments. Login now