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