##// END OF EJS Templates
convert: fix --datesort ordering...
Patrick Mezard -
r6100:49c69e1e default
parent child Browse files
Show More
@@ -0,0 +1,40 b''
1 #!/bin/sh
2
3 cat >> $HGRCPATH <<EOF
4 [extensions]
5 convert=
6 graphlog=
7 EOF
8
9 hg init t
10 cd t
11 echo a >> a
12 hg ci -Am a0 -d '1 0'
13 hg branch brancha
14 echo a >> a
15 hg ci -m a1 -d '2 0'
16 echo a >> a
17 hg ci -m a2 -d '3 0'
18 echo a >> a
19 hg ci -m a3 -d '4 0'
20 hg up -C 0
21 hg branch branchb
22 echo b >> b
23 hg ci -Am b0 -d '5 0'
24 hg up -C brancha
25 echo a >> a
26 hg ci -m a4 -d '6 0'
27 echo a >> a
28 hg ci -m a5 -d '7 0'
29 echo a >> a
30 hg ci -m a6 -d '8 0'
31 hg up -C branchb
32 echo b >> b
33 hg ci -m b1 -d '9 0'
34 cd ..
35
36 echo % convert with datesort
37 hg convert --datesort t t2
38 echo % graph converted repo
39 hg -R t2 glog --template '#rev# "#desc#"\n'
40
@@ -0,0 +1,40 b''
1 adding a
2 marked working directory as branch brancha
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 marked working directory as branch branchb
5 adding b
6 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
7 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
8 % convert with datesort
9 initializing destination t2 repository
10 scanning source...
11 sorting...
12 converting...
13 8 a0
14 7 a1
15 6 a2
16 5 a3
17 4 b0
18 3 a4
19 2 a5
20 1 a6
21 0 b1
22 % graph converted repo
23 o 8 "b1"
24 |
25 | o 7 "a6"
26 | |
27 | o 6 "a5"
28 | |
29 | o 5 "a4"
30 | |
31 o | 4 "b0"
32 | |
33 | o 3 "a3"
34 | |
35 | o 2 "a2"
36 | |
37 | o 1 "a1"
38 |/
39 o 0 "a0"
40
@@ -1,331 +1,342 b''
1 1 # convcmd - convert extension commands definition
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from common import NoRepo, SKIPREV, converter_source, converter_sink
9 9 from cvs import convert_cvs
10 10 from darcs import darcs_source
11 11 from git import convert_git
12 12 from hg import mercurial_source, mercurial_sink
13 13 from subversion import svn_source, debugsvnlog
14 14 import filemap
15 15
16 16 import os, shutil
17 17 from mercurial import hg, util
18 18 from mercurial.i18n import _
19 19
20 20 source_converters = [
21 21 ('cvs', convert_cvs),
22 22 ('git', convert_git),
23 23 ('svn', svn_source),
24 24 ('hg', mercurial_source),
25 25 ('darcs', darcs_source),
26 26 ]
27 27
28 28 sink_converters = [
29 29 ('hg', mercurial_sink),
30 30 ]
31 31
32 32 def convertsource(ui, path, type, rev):
33 33 exceptions = []
34 34 for name, source in source_converters:
35 35 try:
36 36 if not type or name == type:
37 37 return source(ui, path, rev)
38 38 except NoRepo, inst:
39 39 exceptions.append(inst)
40 40 if not ui.quiet:
41 41 for inst in exceptions:
42 42 ui.write(_("%s\n") % inst)
43 43 raise util.Abort('%s: unknown repository type' % path)
44 44
45 45 def convertsink(ui, path, type):
46 46 for name, sink in sink_converters:
47 47 try:
48 48 if not type or name == type:
49 49 return sink(ui, path)
50 50 except NoRepo, inst:
51 51 ui.note(_("convert: %s\n") % inst)
52 52 raise util.Abort('%s: unknown repository type' % path)
53 53
54 54 class converter(object):
55 55 def __init__(self, ui, source, dest, revmapfile, opts):
56 56
57 57 self.source = source
58 58 self.dest = dest
59 59 self.ui = ui
60 60 self.opts = opts
61 61 self.commitcache = {}
62 62 self.revmapfile = revmapfile
63 63 self.revmapfilefd = None
64 64 self.authors = {}
65 65 self.authorfile = None
66 66
67 67 self.maporder = []
68 68 self.map = {}
69 69 try:
70 70 origrevmapfile = open(self.revmapfile, 'r')
71 71 for l in origrevmapfile:
72 72 sv, dv = l[:-1].split()
73 73 if sv not in self.map:
74 74 self.maporder.append(sv)
75 75 self.map[sv] = dv
76 76 origrevmapfile.close()
77 77 except IOError:
78 78 pass
79 79
80 80 # Read first the dst author map if any
81 81 authorfile = self.dest.authorfile()
82 82 if authorfile and os.path.exists(authorfile):
83 83 self.readauthormap(authorfile)
84 84 # Extend/Override with new author map if necessary
85 85 if opts.get('authors'):
86 86 self.readauthormap(opts.get('authors'))
87 87 self.authorfile = self.dest.authorfile()
88 88
89 89 def walktree(self, heads):
90 90 '''Return a mapping that identifies the uncommitted parents of every
91 91 uncommitted changeset.'''
92 92 visit = heads
93 93 known = {}
94 94 parents = {}
95 95 while visit:
96 96 n = visit.pop(0)
97 97 if n in known or n in self.map: continue
98 98 known[n] = 1
99 99 commit = self.cachecommit(n)
100 100 parents[n] = []
101 101 for p in commit.parents:
102 102 parents[n].append(p)
103 103 visit.append(p)
104 104
105 105 return parents
106 106
107 107 def toposort(self, parents):
108 108 '''Return an ordering such that every uncommitted changeset is
109 109 preceeded by all its uncommitted ancestors.'''
110 110 visit = parents.keys()
111 111 seen = {}
112 112 children = {}
113 actives = []
113 114
114 115 while visit:
115 116 n = visit.pop(0)
116 117 if n in seen: continue
117 118 seen[n] = 1
118 119 # Ensure that nodes without parents are present in the 'children'
119 120 # mapping.
120 121 children.setdefault(n, [])
122 hasparent = False
121 123 for p in parents[n]:
122 124 if not p in self.map:
123 125 visit.append(p)
126 hasparent = True
124 127 children.setdefault(p, []).append(n)
128 if not hasparent:
129 actives.append(n)
130
131 del seen
132 del visit
133
134 if self.opts.get('datesort'):
135 dates = {}
136 def getdate(n):
137 if n not in dates:
138 dates[n] = util.parsedate(self.commitcache[n].date)
139 return dates[n]
140
141 def picknext(nodes):
142 return min([(getdate(n), n) for n in nodes])[1]
143 else:
144 prev = [None]
145 def picknext(nodes):
146 # Return the first eligible child of the previously converted
147 # revision, or any of them.
148 next = nodes[0]
149 for n in nodes:
150 if prev[0] in parents[n]:
151 next = n
152 break
153 prev[0] = next
154 return next
125 155
126 156 s = []
127 removed = {}
128 visit = children.keys()
129 while visit:
130 n = visit.pop(0)
131 if n in removed: continue
132 dep = 0
133 if n in parents:
134 for p in parents[n]:
135 if p in self.map: continue
136 if p not in removed:
137 # we're still dependent
138 visit.append(n)
139 dep = 1
140 break
157 pendings = {}
158 while actives:
159 n = picknext(actives)
160 actives.remove(n)
161 s.append(n)
141 162
142 if not dep:
143 # all n's parents are in the list
144 removed[n] = 1
145 if n not in self.map:
146 s.append(n)
147 if n in children:
148 for c in children[n]:
149 visit.insert(0, c)
163 # Update dependents list
164 for c in children.get(n, []):
165 if c not in pendings:
166 pendings[c] = [p for p in parents[c] if p not in self.map]
167 pendings[c].remove(n)
168 if not pendings[c]:
169 # Parents are converted, node is eligible
170 actives.insert(0, c)
171 pendings[c] = None
150 172
151 if self.opts.get('datesort'):
152 depth = {}
153 for n in s:
154 depth[n] = 0
155 pl = [p for p in self.commitcache[n].parents
156 if p not in self.map]
157 if pl:
158 depth[n] = max([depth[p] for p in pl]) + 1
159
160 s = [(depth[n], util.parsedate(self.commitcache[n].date), n)
161 for n in s]
162 s.sort()
163 s = [e[2] for e in s]
173 if len(s) != len(parents):
174 raise util.Abort(_("not all revisions were sorted"))
164 175
165 176 return s
166 177
167 178 def mapentry(self, src, dst):
168 179 if self.revmapfilefd is None:
169 180 try:
170 181 self.revmapfilefd = open(self.revmapfile, "a")
171 182 except IOError, (errno, strerror):
172 183 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.revmapfile, errno, strerror))
173 184 self.map[src] = dst
174 185 self.revmapfilefd.write("%s %s\n" % (src, dst))
175 186 self.revmapfilefd.flush()
176 187
177 188 def writeauthormap(self):
178 189 authorfile = self.authorfile
179 190 if authorfile:
180 191 self.ui.status('Writing author map file %s\n' % authorfile)
181 192 ofile = open(authorfile, 'w+')
182 193 for author in self.authors:
183 194 ofile.write("%s=%s\n" % (author, self.authors[author]))
184 195 ofile.close()
185 196
186 197 def readauthormap(self, authorfile):
187 198 afile = open(authorfile, 'r')
188 199 for line in afile:
189 200 try:
190 201 srcauthor = line.split('=')[0].strip()
191 202 dstauthor = line.split('=')[1].strip()
192 203 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
193 204 self.ui.status(
194 205 'Overriding mapping for author %s, was %s, will be %s\n'
195 206 % (srcauthor, self.authors[srcauthor], dstauthor))
196 207 else:
197 208 self.ui.debug('Mapping author %s to %s\n'
198 209 % (srcauthor, dstauthor))
199 210 self.authors[srcauthor] = dstauthor
200 211 except IndexError:
201 212 self.ui.warn(
202 213 'Ignoring bad line in author file map %s: %s\n'
203 214 % (authorfile, line))
204 215 afile.close()
205 216
206 217 def cachecommit(self, rev):
207 218 commit = self.source.getcommit(rev)
208 219 commit.author = self.authors.get(commit.author, commit.author)
209 220 self.commitcache[rev] = commit
210 221 return commit
211 222
212 223 def copy(self, rev):
213 224 commit = self.commitcache[rev]
214 225 do_copies = hasattr(self.dest, 'copyfile')
215 226 filenames = []
216 227
217 228 changes = self.source.getchanges(rev)
218 229 if isinstance(changes, basestring):
219 230 if changes == SKIPREV:
220 231 dest = SKIPREV
221 232 else:
222 233 dest = self.map[changes]
223 234 self.mapentry(rev, dest)
224 235 return
225 236 files, copies = changes
226 237 pbranches = []
227 238 if commit.parents:
228 239 for prev in commit.parents:
229 240 if prev not in self.commitcache:
230 241 self.cachecommit(prev)
231 242 pbranches.append((self.map[prev],
232 243 self.commitcache[prev].branch))
233 244 self.dest.setbranch(commit.branch, pbranches)
234 245 for f, v in files:
235 246 filenames.append(f)
236 247 try:
237 248 data = self.source.getfile(f, v)
238 249 except IOError, inst:
239 250 self.dest.delfile(f)
240 251 else:
241 252 e = self.source.getmode(f, v)
242 253 self.dest.putfile(f, e, data)
243 254 if do_copies:
244 255 if f in copies:
245 256 copyf = copies[f]
246 257 # Merely marks that a copy happened.
247 258 self.dest.copyfile(copyf, f)
248 259
249 260 parents = [b[0] for b in pbranches]
250 261 newnode = self.dest.putcommit(filenames, parents, commit)
251 262 self.mapentry(rev, newnode)
252 263
253 264 def convert(self):
254 265 try:
255 266 self.source.before()
256 267 self.dest.before()
257 268 self.source.setrevmap(self.map, self.maporder)
258 269 self.ui.status("scanning source...\n")
259 270 heads = self.source.getheads()
260 271 parents = self.walktree(heads)
261 272 self.ui.status("sorting...\n")
262 273 t = self.toposort(parents)
263 274 num = len(t)
264 275 c = None
265 276
266 277 self.ui.status("converting...\n")
267 278 for c in t:
268 279 num -= 1
269 280 desc = self.commitcache[c].desc
270 281 if "\n" in desc:
271 282 desc = desc.splitlines()[0]
272 283 self.ui.status("%d %s\n" % (num, desc))
273 284 self.copy(c)
274 285
275 286 tags = self.source.gettags()
276 287 ctags = {}
277 288 for k in tags:
278 289 v = tags[k]
279 290 if self.map.get(v, SKIPREV) != SKIPREV:
280 291 ctags[k] = self.map[v]
281 292
282 293 if c and ctags:
283 294 nrev = self.dest.puttags(ctags)
284 295 # write another hash correspondence to override the previous
285 296 # one so we don't end up with extra tag heads
286 297 if nrev:
287 298 self.mapentry(c, nrev)
288 299
289 300 self.writeauthormap()
290 301 finally:
291 302 self.cleanup()
292 303
293 304 def cleanup(self):
294 305 try:
295 306 self.dest.after()
296 307 finally:
297 308 self.source.after()
298 309 if self.revmapfilefd:
299 310 self.revmapfilefd.close()
300 311
301 312 def convert(ui, src, dest=None, revmapfile=None, **opts):
302 313 util._encoding = 'UTF-8'
303 314
304 315 if not dest:
305 316 dest = hg.defaultdest(src) + "-hg"
306 317 ui.status("assuming destination %s\n" % dest)
307 318
308 319 destc = convertsink(ui, dest, opts.get('dest_type'))
309 320
310 321 try:
311 322 srcc = convertsource(ui, src, opts.get('source_type'),
312 323 opts.get('rev'))
313 324 except Exception:
314 325 for path in destc.created:
315 326 shutil.rmtree(path, True)
316 327 raise
317 328
318 329 fmap = opts.get('filemap')
319 330 if fmap:
320 331 srcc = filemap.filemap_source(ui, srcc, fmap)
321 332 destc.setfilemapmode(True)
322 333
323 334 if not revmapfile:
324 335 try:
325 336 revmapfile = destc.revmapfile()
326 337 except:
327 338 revmapfile = os.path.join(destc, "map")
328 339
329 340 c = converter(ui, srcc, destc, revmapfile, opts)
330 341 c.convert()
331 342
@@ -1,29 +1,29 b''
1 1 marked working directory as branch branch0
2 2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 3 (branch merge, don't forget to commit)
4 4 % convert
5 5 3 adda
6 6 2 addb
7 7 pulling from default into branch0
8 8 1 changesets found
9 9 1 changea
10 10 0 mergeab
11 11 pulling from default into branch0
12 12 1 changesets found
13 13 marked working directory as branch branch1
14 14 marked working directory as branch branch2
15 15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 16 (branch merge, don't forget to commit)
17 17 marked working directory as branch branch3
18 18 % incremental conversion
19 2 c1
20 pulling from branch0 into branch1
19 2 c2
20 pulling from branch0 into branch2
21 21 2 changesets found
22 1 c2
23 pulling from branch0 into branch2
22 1 c1
23 pulling from branch0 into branch1
24 24 2 changesets found
25 25 0 c3
26 26 pulling from branch2 into branch3
27 27 3 changesets found
28 28 pulling from branch1 into branch3
29 29 1 changesets found
General Comments 0
You need to be logged in to leave comments. Login now