##// END OF EJS Templates
fix typo in convert help text, update test
Benoit Boissinot -
r5488:247af577 default
parent child Browse files
Show More
@@ -1,400 +1,400 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
7
8 from common import NoRepo, SKIPREV, converter_source, converter_sink
8 from common import NoRepo, SKIPREV, converter_source, converter_sink
9 from cvs import convert_cvs
9 from cvs import convert_cvs
10 from darcs import darcs_source
10 from darcs import darcs_source
11 from git import convert_git
11 from git import convert_git
12 from hg import mercurial_source, mercurial_sink
12 from hg import mercurial_source, mercurial_sink
13 from subversion import svn_source, debugsvnlog
13 from subversion import svn_source, debugsvnlog
14 import filemap
14 import filemap
15
15
16 import os, shutil
16 import os, shutil
17 from mercurial import hg, ui, util, commands
17 from mercurial import hg, ui, util, commands
18 from mercurial.i18n import _
18 from mercurial.i18n import _
19
19
20 commands.norepo += " convert debugsvnlog"
20 commands.norepo += " convert debugsvnlog"
21
21
22 source_converters = [
22 source_converters = [
23 ('cvs', convert_cvs),
23 ('cvs', convert_cvs),
24 ('git', convert_git),
24 ('git', convert_git),
25 ('svn', svn_source),
25 ('svn', svn_source),
26 ('hg', mercurial_source),
26 ('hg', mercurial_source),
27 ('darcs', darcs_source),
27 ('darcs', darcs_source),
28 ]
28 ]
29
29
30 sink_converters = [
30 sink_converters = [
31 ('hg', mercurial_sink),
31 ('hg', mercurial_sink),
32 ]
32 ]
33
33
34 def convertsource(ui, path, type, rev):
34 def convertsource(ui, path, type, rev):
35 for name, source in source_converters:
35 for name, source in source_converters:
36 try:
36 try:
37 if not type or name == type:
37 if not type or name == type:
38 return source(ui, path, rev)
38 return source(ui, path, rev)
39 except NoRepo, inst:
39 except NoRepo, inst:
40 ui.note(_("convert: %s\n") % inst)
40 ui.note(_("convert: %s\n") % inst)
41 raise util.Abort('%s: unknown repository type' % path)
41 raise util.Abort('%s: unknown repository type' % path)
42
42
43 def convertsink(ui, path, type):
43 def convertsink(ui, path, type):
44 for name, sink in sink_converters:
44 for name, sink in sink_converters:
45 try:
45 try:
46 if not type or name == type:
46 if not type or name == type:
47 return sink(ui, path)
47 return sink(ui, path)
48 except NoRepo, inst:
48 except NoRepo, inst:
49 ui.note(_("convert: %s\n") % inst)
49 ui.note(_("convert: %s\n") % inst)
50 raise util.Abort('%s: unknown repository type' % path)
50 raise util.Abort('%s: unknown repository type' % path)
51
51
52 class converter(object):
52 class converter(object):
53 def __init__(self, ui, source, dest, revmapfile, opts):
53 def __init__(self, ui, source, dest, revmapfile, opts):
54
54
55 self.source = source
55 self.source = source
56 self.dest = dest
56 self.dest = dest
57 self.ui = ui
57 self.ui = ui
58 self.opts = opts
58 self.opts = opts
59 self.commitcache = {}
59 self.commitcache = {}
60 self.revmapfile = revmapfile
60 self.revmapfile = revmapfile
61 self.revmapfilefd = None
61 self.revmapfilefd = None
62 self.authors = {}
62 self.authors = {}
63 self.authorfile = None
63 self.authorfile = None
64
64
65 self.maporder = []
65 self.maporder = []
66 self.map = {}
66 self.map = {}
67 try:
67 try:
68 origrevmapfile = open(self.revmapfile, 'r')
68 origrevmapfile = open(self.revmapfile, 'r')
69 for l in origrevmapfile:
69 for l in origrevmapfile:
70 sv, dv = l[:-1].split()
70 sv, dv = l[:-1].split()
71 if sv not in self.map:
71 if sv not in self.map:
72 self.maporder.append(sv)
72 self.maporder.append(sv)
73 self.map[sv] = dv
73 self.map[sv] = dv
74 origrevmapfile.close()
74 origrevmapfile.close()
75 except IOError:
75 except IOError:
76 pass
76 pass
77
77
78 # Read first the dst author map if any
78 # Read first the dst author map if any
79 authorfile = self.dest.authorfile()
79 authorfile = self.dest.authorfile()
80 if authorfile and os.path.exists(authorfile):
80 if authorfile and os.path.exists(authorfile):
81 self.readauthormap(authorfile)
81 self.readauthormap(authorfile)
82 # Extend/Override with new author map if necessary
82 # Extend/Override with new author map if necessary
83 if opts.get('authors'):
83 if opts.get('authors'):
84 self.readauthormap(opts.get('authors'))
84 self.readauthormap(opts.get('authors'))
85 self.authorfile = self.dest.authorfile()
85 self.authorfile = self.dest.authorfile()
86
86
87 def walktree(self, heads):
87 def walktree(self, heads):
88 '''Return a mapping that identifies the uncommitted parents of every
88 '''Return a mapping that identifies the uncommitted parents of every
89 uncommitted changeset.'''
89 uncommitted changeset.'''
90 visit = heads
90 visit = heads
91 known = {}
91 known = {}
92 parents = {}
92 parents = {}
93 while visit:
93 while visit:
94 n = visit.pop(0)
94 n = visit.pop(0)
95 if n in known or n in self.map: continue
95 if n in known or n in self.map: continue
96 known[n] = 1
96 known[n] = 1
97 commit = self.cachecommit(n)
97 commit = self.cachecommit(n)
98 parents[n] = []
98 parents[n] = []
99 for p in commit.parents:
99 for p in commit.parents:
100 parents[n].append(p)
100 parents[n].append(p)
101 visit.append(p)
101 visit.append(p)
102
102
103 return parents
103 return parents
104
104
105 def toposort(self, parents):
105 def toposort(self, parents):
106 '''Return an ordering such that every uncommitted changeset is
106 '''Return an ordering such that every uncommitted changeset is
107 preceeded by all its uncommitted ancestors.'''
107 preceeded by all its uncommitted ancestors.'''
108 visit = parents.keys()
108 visit = parents.keys()
109 seen = {}
109 seen = {}
110 children = {}
110 children = {}
111
111
112 while visit:
112 while visit:
113 n = visit.pop(0)
113 n = visit.pop(0)
114 if n in seen: continue
114 if n in seen: continue
115 seen[n] = 1
115 seen[n] = 1
116 # Ensure that nodes without parents are present in the 'children'
116 # Ensure that nodes without parents are present in the 'children'
117 # mapping.
117 # mapping.
118 children.setdefault(n, [])
118 children.setdefault(n, [])
119 for p in parents[n]:
119 for p in parents[n]:
120 if not p in self.map:
120 if not p in self.map:
121 visit.append(p)
121 visit.append(p)
122 children.setdefault(p, []).append(n)
122 children.setdefault(p, []).append(n)
123
123
124 s = []
124 s = []
125 removed = {}
125 removed = {}
126 visit = children.keys()
126 visit = children.keys()
127 while visit:
127 while visit:
128 n = visit.pop(0)
128 n = visit.pop(0)
129 if n in removed: continue
129 if n in removed: continue
130 dep = 0
130 dep = 0
131 if n in parents:
131 if n in parents:
132 for p in parents[n]:
132 for p in parents[n]:
133 if p in self.map: continue
133 if p in self.map: continue
134 if p not in removed:
134 if p not in removed:
135 # we're still dependent
135 # we're still dependent
136 visit.append(n)
136 visit.append(n)
137 dep = 1
137 dep = 1
138 break
138 break
139
139
140 if not dep:
140 if not dep:
141 # all n's parents are in the list
141 # all n's parents are in the list
142 removed[n] = 1
142 removed[n] = 1
143 if n not in self.map:
143 if n not in self.map:
144 s.append(n)
144 s.append(n)
145 if n in children:
145 if n in children:
146 for c in children[n]:
146 for c in children[n]:
147 visit.insert(0, c)
147 visit.insert(0, c)
148
148
149 if self.opts.get('datesort'):
149 if self.opts.get('datesort'):
150 depth = {}
150 depth = {}
151 for n in s:
151 for n in s:
152 depth[n] = 0
152 depth[n] = 0
153 pl = [p for p in self.commitcache[n].parents
153 pl = [p for p in self.commitcache[n].parents
154 if p not in self.map]
154 if p not in self.map]
155 if pl:
155 if pl:
156 depth[n] = max([depth[p] for p in pl]) + 1
156 depth[n] = max([depth[p] for p in pl]) + 1
157
157
158 s = [(depth[n], self.commitcache[n].date, n) for n in s]
158 s = [(depth[n], self.commitcache[n].date, n) for n in s]
159 s.sort()
159 s.sort()
160 s = [e[2] for e in s]
160 s = [e[2] for e in s]
161
161
162 return s
162 return s
163
163
164 def mapentry(self, src, dst):
164 def mapentry(self, src, dst):
165 if self.revmapfilefd is None:
165 if self.revmapfilefd is None:
166 try:
166 try:
167 self.revmapfilefd = open(self.revmapfile, "a")
167 self.revmapfilefd = open(self.revmapfile, "a")
168 except IOError, (errno, strerror):
168 except IOError, (errno, strerror):
169 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.revmapfile, errno, strerror))
169 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.revmapfile, errno, strerror))
170 self.map[src] = dst
170 self.map[src] = dst
171 self.revmapfilefd.write("%s %s\n" % (src, dst))
171 self.revmapfilefd.write("%s %s\n" % (src, dst))
172 self.revmapfilefd.flush()
172 self.revmapfilefd.flush()
173
173
174 def writeauthormap(self):
174 def writeauthormap(self):
175 authorfile = self.authorfile
175 authorfile = self.authorfile
176 if authorfile:
176 if authorfile:
177 self.ui.status('Writing author map file %s\n' % authorfile)
177 self.ui.status('Writing author map file %s\n' % authorfile)
178 ofile = open(authorfile, 'w+')
178 ofile = open(authorfile, 'w+')
179 for author in self.authors:
179 for author in self.authors:
180 ofile.write("%s=%s\n" % (author, self.authors[author]))
180 ofile.write("%s=%s\n" % (author, self.authors[author]))
181 ofile.close()
181 ofile.close()
182
182
183 def readauthormap(self, authorfile):
183 def readauthormap(self, authorfile):
184 afile = open(authorfile, 'r')
184 afile = open(authorfile, 'r')
185 for line in afile:
185 for line in afile:
186 try:
186 try:
187 srcauthor = line.split('=')[0].strip()
187 srcauthor = line.split('=')[0].strip()
188 dstauthor = line.split('=')[1].strip()
188 dstauthor = line.split('=')[1].strip()
189 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
189 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
190 self.ui.status(
190 self.ui.status(
191 'Overriding mapping for author %s, was %s, will be %s\n'
191 'Overriding mapping for author %s, was %s, will be %s\n'
192 % (srcauthor, self.authors[srcauthor], dstauthor))
192 % (srcauthor, self.authors[srcauthor], dstauthor))
193 else:
193 else:
194 self.ui.debug('Mapping author %s to %s\n'
194 self.ui.debug('Mapping author %s to %s\n'
195 % (srcauthor, dstauthor))
195 % (srcauthor, dstauthor))
196 self.authors[srcauthor] = dstauthor
196 self.authors[srcauthor] = dstauthor
197 except IndexError:
197 except IndexError:
198 self.ui.warn(
198 self.ui.warn(
199 'Ignoring bad line in author file map %s: %s\n'
199 'Ignoring bad line in author file map %s: %s\n'
200 % (authorfile, line))
200 % (authorfile, line))
201 afile.close()
201 afile.close()
202
202
203 def cachecommit(self, rev):
203 def cachecommit(self, rev):
204 commit = self.source.getcommit(rev)
204 commit = self.source.getcommit(rev)
205 commit.author = self.authors.get(commit.author, commit.author)
205 commit.author = self.authors.get(commit.author, commit.author)
206 self.commitcache[rev] = commit
206 self.commitcache[rev] = commit
207 return commit
207 return commit
208
208
209 def copy(self, rev):
209 def copy(self, rev):
210 commit = self.commitcache[rev]
210 commit = self.commitcache[rev]
211 do_copies = hasattr(self.dest, 'copyfile')
211 do_copies = hasattr(self.dest, 'copyfile')
212 filenames = []
212 filenames = []
213
213
214 changes = self.source.getchanges(rev)
214 changes = self.source.getchanges(rev)
215 if isinstance(changes, basestring):
215 if isinstance(changes, basestring):
216 if changes == SKIPREV:
216 if changes == SKIPREV:
217 dest = SKIPREV
217 dest = SKIPREV
218 else:
218 else:
219 dest = self.map[changes]
219 dest = self.map[changes]
220 self.mapentry(rev, dest)
220 self.mapentry(rev, dest)
221 return
221 return
222 files, copies = changes
222 files, copies = changes
223 parents = [self.map[r] for r in commit.parents]
223 parents = [self.map[r] for r in commit.parents]
224 if commit.parents:
224 if commit.parents:
225 prev = commit.parents[0]
225 prev = commit.parents[0]
226 if prev not in self.commitcache:
226 if prev not in self.commitcache:
227 self.cachecommit(prev)
227 self.cachecommit(prev)
228 pbranch = self.commitcache[prev].branch
228 pbranch = self.commitcache[prev].branch
229 else:
229 else:
230 pbranch = None
230 pbranch = None
231 self.dest.setbranch(commit.branch, pbranch, parents)
231 self.dest.setbranch(commit.branch, pbranch, parents)
232 for f, v in files:
232 for f, v in files:
233 filenames.append(f)
233 filenames.append(f)
234 try:
234 try:
235 data = self.source.getfile(f, v)
235 data = self.source.getfile(f, v)
236 except IOError, inst:
236 except IOError, inst:
237 self.dest.delfile(f)
237 self.dest.delfile(f)
238 else:
238 else:
239 e = self.source.getmode(f, v)
239 e = self.source.getmode(f, v)
240 self.dest.putfile(f, e, data)
240 self.dest.putfile(f, e, data)
241 if do_copies:
241 if do_copies:
242 if f in copies:
242 if f in copies:
243 copyf = copies[f]
243 copyf = copies[f]
244 # Merely marks that a copy happened.
244 # Merely marks that a copy happened.
245 self.dest.copyfile(copyf, f)
245 self.dest.copyfile(copyf, f)
246
246
247 newnode = self.dest.putcommit(filenames, parents, commit)
247 newnode = self.dest.putcommit(filenames, parents, commit)
248 self.mapentry(rev, newnode)
248 self.mapentry(rev, newnode)
249
249
250 def convert(self):
250 def convert(self):
251 try:
251 try:
252 self.source.before()
252 self.source.before()
253 self.dest.before()
253 self.dest.before()
254 self.source.setrevmap(self.map, self.maporder)
254 self.source.setrevmap(self.map, self.maporder)
255 self.ui.status("scanning source...\n")
255 self.ui.status("scanning source...\n")
256 heads = self.source.getheads()
256 heads = self.source.getheads()
257 parents = self.walktree(heads)
257 parents = self.walktree(heads)
258 self.ui.status("sorting...\n")
258 self.ui.status("sorting...\n")
259 t = self.toposort(parents)
259 t = self.toposort(parents)
260 num = len(t)
260 num = len(t)
261 c = None
261 c = None
262
262
263 self.ui.status("converting...\n")
263 self.ui.status("converting...\n")
264 for c in t:
264 for c in t:
265 num -= 1
265 num -= 1
266 desc = self.commitcache[c].desc
266 desc = self.commitcache[c].desc
267 if "\n" in desc:
267 if "\n" in desc:
268 desc = desc.splitlines()[0]
268 desc = desc.splitlines()[0]
269 self.ui.status("%d %s\n" % (num, desc))
269 self.ui.status("%d %s\n" % (num, desc))
270 self.copy(c)
270 self.copy(c)
271
271
272 tags = self.source.gettags()
272 tags = self.source.gettags()
273 ctags = {}
273 ctags = {}
274 for k in tags:
274 for k in tags:
275 v = tags[k]
275 v = tags[k]
276 if self.map.get(v, SKIPREV) != SKIPREV:
276 if self.map.get(v, SKIPREV) != SKIPREV:
277 ctags[k] = self.map[v]
277 ctags[k] = self.map[v]
278
278
279 if c and ctags:
279 if c and ctags:
280 nrev = self.dest.puttags(ctags)
280 nrev = self.dest.puttags(ctags)
281 # write another hash correspondence to override the previous
281 # write another hash correspondence to override the previous
282 # one so we don't end up with extra tag heads
282 # one so we don't end up with extra tag heads
283 if nrev:
283 if nrev:
284 self.mapentry(c, nrev)
284 self.mapentry(c, nrev)
285
285
286 self.writeauthormap()
286 self.writeauthormap()
287 finally:
287 finally:
288 self.cleanup()
288 self.cleanup()
289
289
290 def cleanup(self):
290 def cleanup(self):
291 try:
291 try:
292 self.dest.after()
292 self.dest.after()
293 finally:
293 finally:
294 self.source.after()
294 self.source.after()
295 if self.revmapfilefd:
295 if self.revmapfilefd:
296 self.revmapfilefd.close()
296 self.revmapfilefd.close()
297
297
298 def convert(ui, src, dest=None, revmapfile=None, **opts):
298 def convert(ui, src, dest=None, revmapfile=None, **opts):
299 """Convert a foreign SCM repository to a Mercurial one.
299 """Convert a foreign SCM repository to a Mercurial one.
300
300
301 Accepted source formats:
301 Accepted source formats:
302 - Mercurial
302 - Mercurial
303 - CVS
303 - CVS
304 - Darcs
304 - Darcs
305 - git
305 - git
306 - Subversion
306 - Subversion
307
307
308 Accepted destination formats:
308 Accepted destination formats:
309 - Mercurial
309 - Mercurial
310
310
311 If no revision is given, all revisions will be converted. Otherwise,
311 If no revision is given, all revisions will be converted. Otherwise,
312 convert will only import up to the named revision (given in a format
312 convert will only import up to the named revision (given in a format
313 understood by the source).
313 understood by the source).
314
314
315 If no destination directory name is specified, it defaults to the
315 If no destination directory name is specified, it defaults to the
316 basename of the source with '-hg' appended. If the destination
316 basename of the source with '-hg' appended. If the destination
317 repository doesn't exist, it will be created.
317 repository doesn't exist, it will be created.
318
318
319 If <MAPFILE> isn't given, it will be put in a default location
319 If <MAPFILE> isn't given, it will be put in a default location
320 (<dest>/.hg/shamap by default). The <MAPFILE> is a simple text
320 (<dest>/.hg/shamap by default). The <MAPFILE> is a simple text
321 file that maps each source commit ID to the destination ID for
321 file that maps each source commit ID to the destination ID for
322 that revision, like so:
322 that revision, like so:
323 <source ID> <destination ID>
323 <source ID> <destination ID>
324
324
325 If the file doesn't exist, it's automatically created. It's updated
325 If the file doesn't exist, it's automatically created. It's updated
326 on each commit copied, so convert-repo can be interrupted and can
326 on each commit copied, so convert-repo can be interrupted and can
327 be run repeatedly to copy new commits.
327 be run repeatedly to copy new commits.
328
328
329 The [username mapping] file is a simple text file that maps each source
329 The [username mapping] file is a simple text file that maps each source
330 commit author to a destination commit author. It is handy for source SCMs
330 commit author to a destination commit author. It is handy for source SCMs
331 that use unix logins to identify authors (eg: CVS). One line per author
331 that use unix logins to identify authors (eg: CVS). One line per author
332 mapping and the line format is:
332 mapping and the line format is:
333 srcauthor=whatever string you want
333 srcauthor=whatever string you want
334
334
335 The filemap is a file that allows filtering and remapping of files
335 The filemap is a file that allows filtering and remapping of files
336 and directories. Comment lines start with '#'. Each line can
336 and directories. Comment lines start with '#'. Each line can
337 contain one of the following directives:
337 contain one of the following directives:
338
338
339 include path/to/file
339 include path/to/file
340
340
341 exclude path/to/file
341 exclude path/to/file
342
342
343 rename from/file to/file
343 rename from/file to/file
344
344
345 The 'include' directive causes a file, or all files under a
345 The 'include' directive causes a file, or all files under a
346 directory, to be included in the destination repository, and the
346 directory, to be included in the destination repository, and the
347 exclussion of all other files and dirs not explicitely included.
347 exclusion of all other files and dirs not explicitely included.
348 The 'exclude' directive causes files or directories to be omitted.
348 The 'exclude' directive causes files or directories to be omitted.
349 The 'rename' directive renames a file or directory. To rename from a
349 The 'rename' directive renames a file or directory. To rename from a
350 subdirectory into the root of the repository, use '.' as the path to
350 subdirectory into the root of the repository, use '.' as the path to
351 rename to.
351 rename to.
352 """
352 """
353
353
354 util._encoding = 'UTF-8'
354 util._encoding = 'UTF-8'
355
355
356 if not dest:
356 if not dest:
357 dest = hg.defaultdest(src) + "-hg"
357 dest = hg.defaultdest(src) + "-hg"
358 ui.status("assuming destination %s\n" % dest)
358 ui.status("assuming destination %s\n" % dest)
359
359
360 destc = convertsink(ui, dest, opts.get('dest_type'))
360 destc = convertsink(ui, dest, opts.get('dest_type'))
361
361
362 try:
362 try:
363 srcc = convertsource(ui, src, opts.get('source_type'),
363 srcc = convertsource(ui, src, opts.get('source_type'),
364 opts.get('rev'))
364 opts.get('rev'))
365 except Exception:
365 except Exception:
366 for path in destc.created:
366 for path in destc.created:
367 shutil.rmtree(path, True)
367 shutil.rmtree(path, True)
368 raise
368 raise
369
369
370 fmap = opts.get('filemap')
370 fmap = opts.get('filemap')
371 if fmap:
371 if fmap:
372 srcc = filemap.filemap_source(ui, srcc, fmap)
372 srcc = filemap.filemap_source(ui, srcc, fmap)
373 destc.setfilemapmode(True)
373 destc.setfilemapmode(True)
374
374
375 if not revmapfile:
375 if not revmapfile:
376 try:
376 try:
377 revmapfile = destc.revmapfile()
377 revmapfile = destc.revmapfile()
378 except:
378 except:
379 revmapfile = os.path.join(destc, "map")
379 revmapfile = os.path.join(destc, "map")
380
380
381 c = converter(ui, srcc, destc, revmapfile, opts)
381 c = converter(ui, srcc, destc, revmapfile, opts)
382 c.convert()
382 c.convert()
383
383
384
384
385 cmdtable = {
385 cmdtable = {
386 "convert":
386 "convert":
387 (convert,
387 (convert,
388 [('A', 'authors', '', 'username mapping filename'),
388 [('A', 'authors', '', 'username mapping filename'),
389 ('d', 'dest-type', '', 'destination repository type'),
389 ('d', 'dest-type', '', 'destination repository type'),
390 ('', 'filemap', '', 'remap file names using contents of file'),
390 ('', 'filemap', '', 'remap file names using contents of file'),
391 ('r', 'rev', '', 'import up to target revision REV'),
391 ('r', 'rev', '', 'import up to target revision REV'),
392 ('s', 'source-type', '', 'source repository type'),
392 ('s', 'source-type', '', 'source repository type'),
393 ('', 'datesort', None, 'try to sort changesets by date')],
393 ('', 'datesort', None, 'try to sort changesets by date')],
394 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
394 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
395 "debugsvnlog":
395 "debugsvnlog":
396 (debugsvnlog,
396 (debugsvnlog,
397 [],
397 [],
398 'hg debugsvnlog'),
398 'hg debugsvnlog'),
399 }
399 }
400
400
@@ -1,93 +1,95 b''
1 hg convert [OPTION]... SOURCE [DEST [MAPFILE]]
1 hg convert [OPTION]... SOURCE [DEST [MAPFILE]]
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:
5 Accepted source formats:
6 - Mercurial
6 - CVS
7 - CVS
7 - Darcs
8 - Darcs
8 - git
9 - git
9 - Subversion
10 - Subversion
10
11
11 Accepted destination formats:
12 Accepted destination formats:
12 - Mercurial
13 - Mercurial
13
14
14 If no revision is given, all revisions will be converted. Otherwise,
15 If no revision is given, all revisions will be converted. Otherwise,
15 convert will only import up to the named revision (given in a format
16 convert will only import up to the named revision (given in a format
16 understood by the source).
17 understood by the source).
17
18
18 If no destination directory name is specified, it defaults to the
19 If no destination directory name is specified, it defaults to the
19 basename of the source with '-hg' appended. If the destination
20 basename of the source with '-hg' appended. If the destination
20 repository doesn't exist, it will be created.
21 repository doesn't exist, it will be created.
21
22
22 If <revmapfile> isn't given, it will be put in a default location
23 If <MAPFILE> isn't given, it will be put in a default location
23 (<dest>/.hg/shamap by default). The <revmapfile> is a simple text
24 (<dest>/.hg/shamap by default). The <MAPFILE> is a simple text
24 file that maps each source commit ID to the destination ID for
25 file that maps each source commit ID to the destination ID for
25 that revision, like so:
26 that revision, like so:
26 <source ID> <destination ID>
27 <source ID> <destination ID>
27
28
28 If the file doesn't exist, it's automatically created. It's updated
29 If the file doesn't exist, it's automatically created. It's updated
29 on each commit copied, so convert-repo can be interrupted and can
30 on each commit copied, so convert-repo can be interrupted and can
30 be run repeatedly to copy new commits.
31 be run repeatedly to copy new commits.
31
32
32 The [username mapping] file is a simple text file that maps each source
33 The [username mapping] file is a simple text file that maps each source
33 commit author to a destination commit author. It is handy for source SCMs
34 commit author to a destination commit author. It is handy for source SCMs
34 that use unix logins to identify authors (eg: CVS). One line per author
35 that use unix logins to identify authors (eg: CVS). One line per author
35 mapping and the line format is:
36 mapping and the line format is:
36 srcauthor=whatever string you want
37 srcauthor=whatever string you want
37
38
38 The filemap is a file that allows filtering and remapping of files
39 The filemap is a file that allows filtering and remapping of files
39 and directories. Comment lines start with '#'. Each line can
40 and directories. Comment lines start with '#'. Each line can
40 contain one of the following directives:
41 contain one of the following directives:
41
42
42 include path/to/file
43 include path/to/file
43
44
44 exclude path/to/file
45 exclude path/to/file
45
46
46 rename from/file to/file
47 rename from/file to/file
47
48
48 The 'include' directive causes a file, or all files under a
49 The 'include' directive causes a file, or all files under a
49 directory, to be included in the destination repository. The
50 directory, to be included in the destination repository, and the
50 'exclude' directive causes files or directories to be omitted.
51 exclusion of all other files and dirs not explicitely included.
51 The 'rename' directive renames a file or directory. To rename
52 The 'exclude' directive causes files or directories to be omitted.
52 from a subdirectory into the root of the repository, use '.' as
53 The 'rename' directive renames a file or directory. To rename from a
53 the path to rename to.
54 subdirectory into the root of the repository, use '.' as the path to
55 rename to.
54
56
55 options:
57 options:
56
58
57 -A --authors username mapping filename
59 -A --authors username mapping filename
58 -d --dest-type destination repository type
60 -d --dest-type destination repository type
59 --filemap remap file names using contents of file
61 --filemap remap file names using contents of file
60 -r --rev import up to target revision REV
62 -r --rev import up to target revision REV
61 -s --source-type source repository type
63 -s --source-type source repository type
62 --datesort try to sort changesets by date
64 --datesort try to sort changesets by date
63
65
64 use "hg -v help convert" to show global options
66 use "hg -v help convert" to show global options
65 adding a
67 adding a
66 assuming destination a-hg
68 assuming destination a-hg
67 initializing destination a-hg repository
69 initializing destination a-hg repository
68 scanning source...
70 scanning source...
69 sorting...
71 sorting...
70 converting...
72 converting...
71 4 a
73 4 a
72 3 b
74 3 b
73 2 c
75 2 c
74 1 d
76 1 d
75 0 e
77 0 e
76 pulling from ../a
78 pulling from ../a
77 searching for changes
79 searching for changes
78 no changes found
80 no changes found
79 % should fail
81 % should fail
80 initializing destination bogusfile repository
82 initializing destination bogusfile repository
81 abort: cannot create new bundle repository
83 abort: cannot create new bundle repository
82 % should fail
84 % should fail
83 abort: Permission denied: bogusdir
85 abort: Permission denied: bogusdir
84 % should succeed
86 % should succeed
85 initializing destination bogusdir repository
87 initializing destination bogusdir repository
86 scanning source...
88 scanning source...
87 sorting...
89 sorting...
88 converting...
90 converting...
89 4 a
91 4 a
90 3 b
92 3 b
91 2 c
93 2 c
92 1 d
94 1 d
93 0 e
95 0 e
General Comments 0
You need to be logged in to leave comments. Login now