##// END OF EJS Templates
convert: remove pdb call. *blush*
Brendan Cully -
r4591:9ec0a3b6 default
parent child Browse files
Show More
@@ -1,315 +1,313 b''
1 # convert.py Foreign SCM converter
1 # convert.py Foreign SCM converter
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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
8 from common import NoRepo
9 from cvs import convert_cvs
9 from cvs import convert_cvs
10 from git import convert_git
10 from git import convert_git
11 from hg import convert_mercurial
11 from hg import convert_mercurial
12
12
13 import os
13 import os
14 from mercurial import hg, ui, util, commands
14 from mercurial import hg, ui, util, commands
15
15
16 commands.norepo += " convert"
16 commands.norepo += " convert"
17
17
18 converters = [convert_cvs, convert_git, convert_mercurial]
18 converters = [convert_cvs, convert_git, convert_mercurial]
19
19
20 def converter(ui, path):
20 def converter(ui, path):
21 if not os.path.isdir(path):
21 if not os.path.isdir(path):
22 raise util.Abort("%s: not a directory" % path)
22 raise util.Abort("%s: not a directory" % path)
23 for c in converters:
23 for c in converters:
24 try:
24 try:
25 return c(ui, path)
25 return c(ui, path)
26 except NoRepo:
26 except NoRepo:
27 pass
27 pass
28 raise util.Abort("%s: unknown repository type" % path)
28 raise util.Abort("%s: unknown repository type" % path)
29
29
30 class convert(object):
30 class convert(object):
31 def __init__(self, ui, source, dest, mapfile, opts):
31 def __init__(self, ui, source, dest, mapfile, opts):
32
32
33 self.source = source
33 self.source = source
34 self.dest = dest
34 self.dest = dest
35 self.ui = ui
35 self.ui = ui
36 self.opts = opts
36 self.opts = opts
37 self.commitcache = {}
37 self.commitcache = {}
38 self.mapfile = mapfile
38 self.mapfile = mapfile
39 self.mapfilefd = None
39 self.mapfilefd = None
40 self.authors = {}
40 self.authors = {}
41 self.authorfile = None
41 self.authorfile = None
42
42
43 self.map = {}
43 self.map = {}
44 try:
44 try:
45 origmapfile = open(self.mapfile, 'r')
45 origmapfile = open(self.mapfile, 'r')
46 for l in origmapfile:
46 for l in origmapfile:
47 sv, dv = l[:-1].split()
47 sv, dv = l[:-1].split()
48 self.map[sv] = dv
48 self.map[sv] = dv
49 origmapfile.close()
49 origmapfile.close()
50 except IOError:
50 except IOError:
51 pass
51 pass
52
52
53 # Read first the dst author map if any
53 # Read first the dst author map if any
54 authorfile = self.dest.authorfile()
54 authorfile = self.dest.authorfile()
55 if authorfile and os.path.exists(authorfile):
55 if authorfile and os.path.exists(authorfile):
56 self.readauthormap(authorfile)
56 self.readauthormap(authorfile)
57 # Extend/Override with new author map if necessary
57 # Extend/Override with new author map if necessary
58 if opts.get('authors'):
58 if opts.get('authors'):
59 import pdb
60 pdb.set_trace()
61 self.readauthormap(opts.get('authors'))
59 self.readauthormap(opts.get('authors'))
62 self.authorfile = self.dest.authorfile()
60 self.authorfile = self.dest.authorfile()
63
61
64 def walktree(self, heads):
62 def walktree(self, heads):
65 visit = heads
63 visit = heads
66 known = {}
64 known = {}
67 parents = {}
65 parents = {}
68 while visit:
66 while visit:
69 n = visit.pop(0)
67 n = visit.pop(0)
70 if n in known or n in self.map: continue
68 if n in known or n in self.map: continue
71 known[n] = 1
69 known[n] = 1
72 self.commitcache[n] = self.source.getcommit(n)
70 self.commitcache[n] = self.source.getcommit(n)
73 cp = self.commitcache[n].parents
71 cp = self.commitcache[n].parents
74 for p in cp:
72 for p in cp:
75 parents.setdefault(n, []).append(p)
73 parents.setdefault(n, []).append(p)
76 visit.append(p)
74 visit.append(p)
77
75
78 return parents
76 return parents
79
77
80 def toposort(self, parents):
78 def toposort(self, parents):
81 visit = parents.keys()
79 visit = parents.keys()
82 seen = {}
80 seen = {}
83 children = {}
81 children = {}
84
82
85 while visit:
83 while visit:
86 n = visit.pop(0)
84 n = visit.pop(0)
87 if n in seen: continue
85 if n in seen: continue
88 seen[n] = 1
86 seen[n] = 1
89 pc = 0
87 pc = 0
90 if n in parents:
88 if n in parents:
91 for p in parents[n]:
89 for p in parents[n]:
92 if p not in self.map: pc += 1
90 if p not in self.map: pc += 1
93 visit.append(p)
91 visit.append(p)
94 children.setdefault(p, []).append(n)
92 children.setdefault(p, []).append(n)
95 if not pc: root = n
93 if not pc: root = n
96
94
97 s = []
95 s = []
98 removed = {}
96 removed = {}
99 visit = children.keys()
97 visit = children.keys()
100 while visit:
98 while visit:
101 n = visit.pop(0)
99 n = visit.pop(0)
102 if n in removed: continue
100 if n in removed: continue
103 dep = 0
101 dep = 0
104 if n in parents:
102 if n in parents:
105 for p in parents[n]:
103 for p in parents[n]:
106 if p in self.map: continue
104 if p in self.map: continue
107 if p not in removed:
105 if p not in removed:
108 # we're still dependent
106 # we're still dependent
109 visit.append(n)
107 visit.append(n)
110 dep = 1
108 dep = 1
111 break
109 break
112
110
113 if not dep:
111 if not dep:
114 # all n's parents are in the list
112 # all n's parents are in the list
115 removed[n] = 1
113 removed[n] = 1
116 if n not in self.map:
114 if n not in self.map:
117 s.append(n)
115 s.append(n)
118 if n in children:
116 if n in children:
119 for c in children[n]:
117 for c in children[n]:
120 visit.insert(0, c)
118 visit.insert(0, c)
121
119
122 if self.opts.get('datesort'):
120 if self.opts.get('datesort'):
123 depth = {}
121 depth = {}
124 for n in s:
122 for n in s:
125 depth[n] = 0
123 depth[n] = 0
126 pl = [p for p in self.commitcache[n].parents
124 pl = [p for p in self.commitcache[n].parents
127 if p not in self.map]
125 if p not in self.map]
128 if pl:
126 if pl:
129 depth[n] = max([depth[p] for p in pl]) + 1
127 depth[n] = max([depth[p] for p in pl]) + 1
130
128
131 s = [(depth[n], self.commitcache[n].date, n) for n in s]
129 s = [(depth[n], self.commitcache[n].date, n) for n in s]
132 s.sort()
130 s.sort()
133 s = [e[2] for e in s]
131 s = [e[2] for e in s]
134
132
135 return s
133 return s
136
134
137 def mapentry(self, src, dst):
135 def mapentry(self, src, dst):
138 if self.mapfilefd is None:
136 if self.mapfilefd is None:
139 try:
137 try:
140 self.mapfilefd = open(self.mapfile, "a")
138 self.mapfilefd = open(self.mapfile, "a")
141 except IOError, (errno, strerror):
139 except IOError, (errno, strerror):
142 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
140 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
143 self.map[src] = dst
141 self.map[src] = dst
144 self.mapfilefd.write("%s %s\n" % (src, dst))
142 self.mapfilefd.write("%s %s\n" % (src, dst))
145 self.mapfilefd.flush()
143 self.mapfilefd.flush()
146
144
147 def writeauthormap(self):
145 def writeauthormap(self):
148 authorfile = self.authorfile
146 authorfile = self.authorfile
149 if authorfile:
147 if authorfile:
150 self.ui.status('Writing author map file %s\n' % authorfile)
148 self.ui.status('Writing author map file %s\n' % authorfile)
151 ofile = open(authorfile, 'w+')
149 ofile = open(authorfile, 'w+')
152 for author in self.authors:
150 for author in self.authors:
153 ofile.write("%s=%s\n" % (author, self.authors[author]))
151 ofile.write("%s=%s\n" % (author, self.authors[author]))
154 ofile.close()
152 ofile.close()
155
153
156 def readauthormap(self, authorfile):
154 def readauthormap(self, authorfile):
157 afile = open(authorfile, 'r')
155 afile = open(authorfile, 'r')
158 for line in afile:
156 for line in afile:
159 try:
157 try:
160 srcauthor = line.split('=')[0].strip()
158 srcauthor = line.split('=')[0].strip()
161 dstauthor = line.split('=')[1].strip()
159 dstauthor = line.split('=')[1].strip()
162 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
160 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
163 self.ui.status(
161 self.ui.status(
164 'Overriding mapping for author %s, was %s, will be %s\n'
162 'Overriding mapping for author %s, was %s, will be %s\n'
165 % (srcauthor, self.authors[srcauthor], dstauthor))
163 % (srcauthor, self.authors[srcauthor], dstauthor))
166 else:
164 else:
167 self.ui.debug('Mapping author %s to %s\n'
165 self.ui.debug('Mapping author %s to %s\n'
168 % (srcauthor, dstauthor))
166 % (srcauthor, dstauthor))
169 self.authors[srcauthor] = dstauthor
167 self.authors[srcauthor] = dstauthor
170 except IndexError:
168 except IndexError:
171 self.ui.warn(
169 self.ui.warn(
172 'Ignoring bad line in author file map %s: %s\n'
170 'Ignoring bad line in author file map %s: %s\n'
173 % (authorfile, line))
171 % (authorfile, line))
174 afile.close()
172 afile.close()
175
173
176 def copy(self, rev):
174 def copy(self, rev):
177 c = self.commitcache[rev]
175 c = self.commitcache[rev]
178 files = self.source.getchanges(rev)
176 files = self.source.getchanges(rev)
179
177
180 for f, v in files:
178 for f, v in files:
181 try:
179 try:
182 data = self.source.getfile(f, v)
180 data = self.source.getfile(f, v)
183 except IOError, inst:
181 except IOError, inst:
184 self.dest.delfile(f)
182 self.dest.delfile(f)
185 else:
183 else:
186 e = self.source.getmode(f, v)
184 e = self.source.getmode(f, v)
187 self.dest.putfile(f, e, data)
185 self.dest.putfile(f, e, data)
188
186
189 r = [self.map[v] for v in c.parents]
187 r = [self.map[v] for v in c.parents]
190 f = [f for f, v in files]
188 f = [f for f, v in files]
191 newnode = self.dest.putcommit(f, r, c)
189 newnode = self.dest.putcommit(f, r, c)
192 self.mapentry(rev, newnode)
190 self.mapentry(rev, newnode)
193
191
194 def convert(self):
192 def convert(self):
195 try:
193 try:
196 self.ui.status("scanning source...\n")
194 self.ui.status("scanning source...\n")
197 heads = self.source.getheads()
195 heads = self.source.getheads()
198 parents = self.walktree(heads)
196 parents = self.walktree(heads)
199 self.ui.status("sorting...\n")
197 self.ui.status("sorting...\n")
200 t = self.toposort(parents)
198 t = self.toposort(parents)
201 num = len(t)
199 num = len(t)
202 c = None
200 c = None
203
201
204 self.ui.status("converting...\n")
202 self.ui.status("converting...\n")
205 for c in t:
203 for c in t:
206 num -= 1
204 num -= 1
207 desc = self.commitcache[c].desc
205 desc = self.commitcache[c].desc
208 if "\n" in desc:
206 if "\n" in desc:
209 desc = desc.splitlines()[0]
207 desc = desc.splitlines()[0]
210 author = self.commitcache[c].author
208 author = self.commitcache[c].author
211 author = self.authors.get(author, author)
209 author = self.authors.get(author, author)
212 self.commitcache[c].author = author
210 self.commitcache[c].author = author
213 self.ui.status("%d %s\n" % (num, desc))
211 self.ui.status("%d %s\n" % (num, desc))
214 self.copy(c)
212 self.copy(c)
215
213
216 tags = self.source.gettags()
214 tags = self.source.gettags()
217 ctags = {}
215 ctags = {}
218 for k in tags:
216 for k in tags:
219 v = tags[k]
217 v = tags[k]
220 if v in self.map:
218 if v in self.map:
221 ctags[k] = self.map[v]
219 ctags[k] = self.map[v]
222
220
223 if c and ctags:
221 if c and ctags:
224 nrev = self.dest.puttags(ctags)
222 nrev = self.dest.puttags(ctags)
225 # write another hash correspondence to override the previous
223 # write another hash correspondence to override the previous
226 # one so we don't end up with extra tag heads
224 # one so we don't end up with extra tag heads
227 if nrev:
225 if nrev:
228 self.mapentry(c, nrev)
226 self.mapentry(c, nrev)
229
227
230 self.writeauthormap()
228 self.writeauthormap()
231 finally:
229 finally:
232 self.cleanup()
230 self.cleanup()
233
231
234 def cleanup(self):
232 def cleanup(self):
235 if self.mapfilefd:
233 if self.mapfilefd:
236 self.mapfilefd.close()
234 self.mapfilefd.close()
237
235
238 def _convert(ui, src, dest=None, mapfile=None, **opts):
236 def _convert(ui, src, dest=None, mapfile=None, **opts):
239 '''Convert a foreign SCM repository to a Mercurial one.
237 '''Convert a foreign SCM repository to a Mercurial one.
240
238
241 Accepted source formats:
239 Accepted source formats:
242 - GIT
240 - GIT
243 - CVS
241 - CVS
244
242
245 Accepted destination formats:
243 Accepted destination formats:
246 - Mercurial
244 - Mercurial
247
245
248 If destination isn't given, a new Mercurial repo named <src>-hg will
246 If destination isn't given, a new Mercurial repo named <src>-hg will
249 be created. If <mapfile> isn't given, it will be put in a default
247 be created. If <mapfile> isn't given, it will be put in a default
250 location (<dest>/.hg/shamap by default)
248 location (<dest>/.hg/shamap by default)
251
249
252 The <mapfile> is a simple text file that maps each source commit ID to
250 The <mapfile> is a simple text file that maps each source commit ID to
253 the destination ID for that revision, like so:
251 the destination ID for that revision, like so:
254 <source ID> <destination ID>
252 <source ID> <destination ID>
255
253
256 If the file doesn't exist, it's automatically created. It's updated
254 If the file doesn't exist, it's automatically created. It's updated
257 on each commit copied, so convert-repo can be interrupted and can
255 on each commit copied, so convert-repo can be interrupted and can
258 be run repeatedly to copy new commits.
256 be run repeatedly to copy new commits.
259
257
260 The [username mapping] file is a simple text file that maps each source
258 The [username mapping] file is a simple text file that maps each source
261 commit author to a destination commit author. It is handy for source SCMs
259 commit author to a destination commit author. It is handy for source SCMs
262 that use unix logins to identify authors (eg: CVS). One line per author
260 that use unix logins to identify authors (eg: CVS). One line per author
263 mapping and the line format is:
261 mapping and the line format is:
264 srcauthor=whatever string you want
262 srcauthor=whatever string you want
265 '''
263 '''
266
264
267 srcc = converter(ui, src)
265 srcc = converter(ui, src)
268 if not hasattr(srcc, "getcommit"):
266 if not hasattr(srcc, "getcommit"):
269 raise util.Abort("%s: can't read from this repo type" % src)
267 raise util.Abort("%s: can't read from this repo type" % src)
270
268
271 if not dest:
269 if not dest:
272 dest = src + "-hg"
270 dest = src + "-hg"
273 ui.status("assuming destination %s\n" % dest)
271 ui.status("assuming destination %s\n" % dest)
274
272
275 # Try to be smart and initalize things when required
273 # Try to be smart and initalize things when required
276 if os.path.isdir(dest):
274 if os.path.isdir(dest):
277 if len(os.listdir(dest)) > 0:
275 if len(os.listdir(dest)) > 0:
278 try:
276 try:
279 hg.repository(ui, dest)
277 hg.repository(ui, dest)
280 ui.status("destination %s is a Mercurial repository\n" % dest)
278 ui.status("destination %s is a Mercurial repository\n" % dest)
281 except hg.RepoError:
279 except hg.RepoError:
282 raise util.Abort(
280 raise util.Abort(
283 "destination directory %s is not empty.\n"
281 "destination directory %s is not empty.\n"
284 "Please specify an empty directory to be initialized\n"
282 "Please specify an empty directory to be initialized\n"
285 "or an already initialized mercurial repository"
283 "or an already initialized mercurial repository"
286 % dest)
284 % dest)
287 else:
285 else:
288 ui.status("initializing destination %s repository\n" % dest)
286 ui.status("initializing destination %s repository\n" % dest)
289 hg.repository(ui, dest, create=True)
287 hg.repository(ui, dest, create=True)
290 elif os.path.exists(dest):
288 elif os.path.exists(dest):
291 raise util.Abort("destination %s exists and is not a directory" % dest)
289 raise util.Abort("destination %s exists and is not a directory" % dest)
292 else:
290 else:
293 ui.status("initializing destination %s repository\n" % dest)
291 ui.status("initializing destination %s repository\n" % dest)
294 hg.repository(ui, dest, create=True)
292 hg.repository(ui, dest, create=True)
295
293
296 destc = converter(ui, dest)
294 destc = converter(ui, dest)
297 if not hasattr(destc, "putcommit"):
295 if not hasattr(destc, "putcommit"):
298 raise util.Abort("%s: can't write to this repo type" % src)
296 raise util.Abort("%s: can't write to this repo type" % src)
299
297
300 if not mapfile:
298 if not mapfile:
301 try:
299 try:
302 mapfile = destc.mapfile()
300 mapfile = destc.mapfile()
303 except:
301 except:
304 mapfile = os.path.join(destc, "map")
302 mapfile = os.path.join(destc, "map")
305
303
306 c = convert(ui, srcc, destc, mapfile, opts)
304 c = convert(ui, srcc, destc, mapfile, opts)
307 c.convert()
305 c.convert()
308
306
309 cmdtable = {
307 cmdtable = {
310 "convert":
308 "convert":
311 (_convert,
309 (_convert,
312 [('A', 'authors', '', 'username mapping filename'),
310 [('A', 'authors', '', 'username mapping filename'),
313 ('', 'datesort', None, 'try to sort changesets by date')],
311 ('', 'datesort', None, 'try to sort changesets by date')],
314 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
312 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
315 }
313 }
General Comments 0
You need to be logged in to leave comments. Login now