##// END OF EJS Templates
convert: better feedback when filtering out empty revisions...
Patrick Mezard -
r8611:ba42e3c6 default
parent child Browse files
Show More
@@ -1,338 +1,339 b''
1 1 # hg.py - hg backend for convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 # Notes for hg->hg conversion:
9 9 #
10 10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 11 # of commit messages, but new versions do. Changesets created by
12 12 # those older versions, then converted, may thus have different
13 13 # hashes for changesets that are otherwise identical.
14 14 #
15 15 # * Using "--config convert.hg.saverev=true" will make the source
16 16 # identifier to be stored in the converted revision. This will cause
17 17 # the converted revision to have a different identity than the
18 18 # source.
19 19
20 20
21 21 import os, time
22 22 from mercurial.i18n import _
23 23 from mercurial.node import bin, hex, nullid
24 24 from mercurial import hg, util, context, error
25 25
26 26 from common import NoRepo, commit, converter_source, converter_sink
27 27
28 28 class mercurial_sink(converter_sink):
29 29 def __init__(self, ui, path):
30 30 converter_sink.__init__(self, ui, path)
31 31 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
32 32 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
33 33 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
34 34 self.lastbranch = None
35 35 if os.path.isdir(path) and len(os.listdir(path)) > 0:
36 36 try:
37 37 self.repo = hg.repository(self.ui, path)
38 38 if not self.repo.local():
39 39 raise NoRepo(_('%s is not a local Mercurial repo') % path)
40 40 except error.RepoError, err:
41 41 ui.traceback()
42 42 raise NoRepo(err.args[0])
43 43 else:
44 44 try:
45 45 ui.status(_('initializing destination %s repository\n') % path)
46 46 self.repo = hg.repository(self.ui, path, create=True)
47 47 if not self.repo.local():
48 48 raise NoRepo(_('%s is not a local Mercurial repo') % path)
49 49 self.created.append(path)
50 50 except error.RepoError:
51 51 ui.traceback()
52 52 raise NoRepo("could not create hg repo %s as sink" % path)
53 53 self.lock = None
54 54 self.wlock = None
55 55 self.filemapmode = False
56 56
57 57 def before(self):
58 58 self.ui.debug(_('run hg sink pre-conversion action\n'))
59 59 self.wlock = self.repo.wlock()
60 60 self.lock = self.repo.lock()
61 61
62 62 def after(self):
63 63 self.ui.debug(_('run hg sink post-conversion action\n'))
64 64 self.lock.release()
65 65 self.wlock.release()
66 66
67 67 def revmapfile(self):
68 68 return os.path.join(self.path, ".hg", "shamap")
69 69
70 70 def authorfile(self):
71 71 return os.path.join(self.path, ".hg", "authormap")
72 72
73 73 def getheads(self):
74 74 h = self.repo.changelog.heads()
75 75 return [ hex(x) for x in h ]
76 76
77 77 def setbranch(self, branch, pbranches):
78 78 if not self.clonebranches:
79 79 return
80 80
81 81 setbranch = (branch != self.lastbranch)
82 82 self.lastbranch = branch
83 83 if not branch:
84 84 branch = 'default'
85 85 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
86 86 pbranch = pbranches and pbranches[0][1] or 'default'
87 87
88 88 branchpath = os.path.join(self.path, branch)
89 89 if setbranch:
90 90 self.after()
91 91 try:
92 92 self.repo = hg.repository(self.ui, branchpath)
93 93 except:
94 94 self.repo = hg.repository(self.ui, branchpath, create=True)
95 95 self.before()
96 96
97 97 # pbranches may bring revisions from other branches (merge parents)
98 98 # Make sure we have them, or pull them.
99 99 missings = {}
100 100 for b in pbranches:
101 101 try:
102 102 self.repo.lookup(b[0])
103 103 except:
104 104 missings.setdefault(b[1], []).append(b[0])
105 105
106 106 if missings:
107 107 self.after()
108 108 for pbranch, heads in missings.iteritems():
109 109 pbranchpath = os.path.join(self.path, pbranch)
110 110 prepo = hg.repository(self.ui, pbranchpath)
111 111 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
112 112 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
113 113 self.before()
114 114
115 115 def putcommit(self, files, copies, parents, commit, source):
116 116
117 117 files = dict(files)
118 118 def getfilectx(repo, memctx, f):
119 119 v = files[f]
120 120 data = source.getfile(f, v)
121 121 e = source.getmode(f, v)
122 122 return context.memfilectx(f, data, 'l' in e, 'x' in e, copies.get(f))
123 123
124 124 pl = []
125 125 for p in parents:
126 126 if p not in pl:
127 127 pl.append(p)
128 128 parents = pl
129 129 nparents = len(parents)
130 130 if self.filemapmode and nparents == 1:
131 131 m1node = self.repo.changelog.read(bin(parents[0]))[0]
132 132 parent = parents[0]
133 133
134 134 if len(parents) < 2: parents.append(nullid)
135 135 if len(parents) < 2: parents.append(nullid)
136 136 p2 = parents.pop(0)
137 137
138 138 text = commit.desc
139 139 extra = commit.extra.copy()
140 140 if self.branchnames and commit.branch:
141 141 extra['branch'] = commit.branch
142 142 if commit.rev:
143 143 extra['convert_revision'] = commit.rev
144 144
145 145 while parents:
146 146 p1 = p2
147 147 p2 = parents.pop(0)
148 148 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(), getfilectx,
149 149 commit.author, commit.date, extra)
150 150 self.repo.commitctx(ctx)
151 151 text = "(octopus merge fixup)\n"
152 152 p2 = hex(self.repo.changelog.tip())
153 153
154 154 if self.filemapmode and nparents == 1:
155 155 man = self.repo.manifest
156 156 mnode = self.repo.changelog.read(bin(p2))[0]
157 157 if not man.cmp(m1node, man.revision(mnode)):
158 self.ui.status(_("filtering out empty revision\n"))
158 159 self.repo.rollback()
159 160 return parent
160 161 return p2
161 162
162 163 def puttags(self, tags):
163 164 try:
164 165 parentctx = self.repo[self.tagsbranch]
165 166 tagparent = parentctx.node()
166 167 except error.RepoError:
167 168 parentctx = None
168 169 tagparent = nullid
169 170
170 171 try:
171 172 oldlines = sorted(parentctx['.hgtags'].data().splitlines(1))
172 173 except:
173 174 oldlines = []
174 175
175 176 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
176 177 if newlines == oldlines:
177 178 return None
178 179 data = "".join(newlines)
179 180 def getfilectx(repo, memctx, f):
180 181 return context.memfilectx(f, data, False, False, None)
181 182
182 183 self.ui.status(_("updating tags\n"))
183 184 date = "%s 0" % int(time.mktime(time.gmtime()))
184 185 extra = {'branch': self.tagsbranch}
185 186 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
186 187 [".hgtags"], getfilectx, "convert-repo", date,
187 188 extra)
188 189 self.repo.commitctx(ctx)
189 190 return hex(self.repo.changelog.tip())
190 191
191 192 def setfilemapmode(self, active):
192 193 self.filemapmode = active
193 194
194 195 class mercurial_source(converter_source):
195 196 def __init__(self, ui, path, rev=None):
196 197 converter_source.__init__(self, ui, path, rev)
197 198 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
198 199 self.ignored = set()
199 200 self.saverev = ui.configbool('convert', 'hg.saverev', False)
200 201 try:
201 202 self.repo = hg.repository(self.ui, path)
202 203 # try to provoke an exception if this isn't really a hg
203 204 # repo, but some other bogus compatible-looking url
204 205 if not self.repo.local():
205 206 raise error.RepoError()
206 207 except error.RepoError:
207 208 ui.traceback()
208 209 raise NoRepo("%s is not a local Mercurial repo" % path)
209 210 self.lastrev = None
210 211 self.lastctx = None
211 212 self._changescache = None
212 213 self.convertfp = None
213 214 # Restrict converted revisions to startrev descendants
214 215 startnode = ui.config('convert', 'hg.startrev')
215 216 if startnode is not None:
216 217 try:
217 218 startnode = self.repo.lookup(startnode)
218 219 except error.RepoError:
219 220 raise util.Abort(_('%s is not a valid start revision')
220 221 % startnode)
221 222 startrev = self.repo.changelog.rev(startnode)
222 223 children = {startnode: 1}
223 224 for rev in self.repo.changelog.descendants(startrev):
224 225 children[self.repo.changelog.node(rev)] = 1
225 226 self.keep = children.__contains__
226 227 else:
227 228 self.keep = util.always
228 229
229 230 def changectx(self, rev):
230 231 if self.lastrev != rev:
231 232 self.lastctx = self.repo[rev]
232 233 self.lastrev = rev
233 234 return self.lastctx
234 235
235 236 def parents(self, ctx):
236 237 return [p.node() for p in ctx.parents()
237 238 if p and self.keep(p.node())]
238 239
239 240 def getheads(self):
240 241 if self.rev:
241 242 heads = [self.repo[self.rev].node()]
242 243 else:
243 244 heads = self.repo.heads()
244 245 return [hex(h) for h in heads if self.keep(h)]
245 246
246 247 def getfile(self, name, rev):
247 248 try:
248 249 return self.changectx(rev)[name].data()
249 250 except error.LookupError, err:
250 251 raise IOError(err)
251 252
252 253 def getmode(self, name, rev):
253 254 return self.changectx(rev).manifest().flags(name)
254 255
255 256 def getchanges(self, rev):
256 257 ctx = self.changectx(rev)
257 258 parents = self.parents(ctx)
258 259 if not parents:
259 260 files = sorted(ctx.manifest())
260 261 if self.ignoreerrors:
261 262 # calling getcopies() is a simple way to detect missing
262 263 # revlogs and populate self.ignored
263 264 self.getcopies(ctx, files)
264 265 return [(f, rev) for f in files if f not in self.ignored], {}
265 266 if self._changescache and self._changescache[0] == rev:
266 267 m, a, r = self._changescache[1]
267 268 else:
268 269 m, a, r = self.repo.status(parents[0], ctx.node())[:3]
269 270 # getcopies() detects missing revlogs early, run it before
270 271 # filtering the changes.
271 272 copies = self.getcopies(ctx, m + a)
272 273 changes = [(name, rev) for name in m + a + r
273 274 if name not in self.ignored]
274 275 return sorted(changes), copies
275 276
276 277 def getcopies(self, ctx, files):
277 278 copies = {}
278 279 for name in files:
279 280 if name in self.ignored:
280 281 continue
281 282 try:
282 283 copysource, copynode = ctx.filectx(name).renamed()
283 284 if copysource in self.ignored or not self.keep(copynode):
284 285 continue
285 286 copies[name] = copysource
286 287 except TypeError:
287 288 pass
288 289 except error.LookupError, e:
289 290 if not self.ignoreerrors:
290 291 raise
291 292 self.ignored.add(name)
292 293 self.ui.warn(_('ignoring: %s\n') % e)
293 294 return copies
294 295
295 296 def getcommit(self, rev):
296 297 ctx = self.changectx(rev)
297 298 parents = [hex(p) for p in self.parents(ctx)]
298 299 if self.saverev:
299 300 crev = rev
300 301 else:
301 302 crev = None
302 303 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
303 304 desc=ctx.description(), rev=crev, parents=parents,
304 305 branch=ctx.branch(), extra=ctx.extra())
305 306
306 307 def gettags(self):
307 308 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
308 309 return dict([(name, hex(node)) for name, node in tags
309 310 if self.keep(node)])
310 311
311 312 def getchangedfiles(self, rev, i):
312 313 ctx = self.changectx(rev)
313 314 parents = self.parents(ctx)
314 315 if not parents and i is None:
315 316 i = 0
316 317 changes = [], ctx.manifest().keys(), []
317 318 else:
318 319 i = i or 0
319 320 changes = self.repo.status(parents[i], ctx.node())[:3]
320 321 changes = [[f for f in l if f not in self.ignored] for l in changes]
321 322
322 323 if i == 0:
323 324 self._changescache = (rev, changes)
324 325
325 326 return changes[0] + changes[1] + changes[2]
326 327
327 328 def converted(self, rev, destrev):
328 329 if self.convertfp is None:
329 330 self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
330 331 'a')
331 332 self.convertfp.write('%s %s\n' % (destrev, rev))
332 333 self.convertfp.flush()
333 334
334 335 def before(self):
335 336 self.ui.debug(_('run hg source pre-conversion action\n'))
336 337
337 338 def after(self):
338 339 self.ui.debug(_('run hg source post-conversion action\n'))
@@ -1,236 +1,237 b''
1 1 % create cvs repository
2 2 % create source directory
3 3 % import source directory
4 4 N src/a
5 5 N src/b/c
6 6
7 7 No conflicts created by this import
8 8
9 9 % checkout source directory
10 10 U src/a
11 11 U src/b/c
12 12 % commit a new revision changing b/c
13 13 checking in src/b/c,v
14 14 % convert fresh repo
15 15 initializing destination src-hg repository
16 16 connecting to cvsrepo
17 17 scanning source...
18 18 using builtin cvsps
19 19 collecting CVS rlog
20 20 5 log entries
21 21 creating changesets
22 22 3 changeset entries
23 23 sorting...
24 24 converting...
25 25 2 Initial revision
26 26 1 import
27 27 0 ci0
28 28 updating tags
29 29 a
30 30 c
31 31 c
32 32 % convert fresh repo with --filemap
33 33 initializing destination src-filemap repository
34 34 connecting to cvsrepo
35 35 scanning source...
36 36 using builtin cvsps
37 37 collecting CVS rlog
38 38 5 log entries
39 39 creating changesets
40 40 3 changeset entries
41 41 sorting...
42 42 converting...
43 43 2 Initial revision
44 44 1 import
45 filtering out empty revision
45 46 rolling back last transaction
46 47 0 ci0
47 48 updating tags
48 49 c
49 50 c
50 51 2 update tags files: .hgtags
51 52 1 ci0 files: b/c
52 53 0 Initial revision files: b/c
53 54 % commit new file revisions
54 55 checking in src/a,v
55 56 checking in src/b/c,v
56 57 % convert again
57 58 connecting to cvsrepo
58 59 scanning source...
59 60 using builtin cvsps
60 61 collecting CVS rlog
61 62 7 log entries
62 63 creating changesets
63 64 4 changeset entries
64 65 sorting...
65 66 converting...
66 67 0 ci1
67 68 a
68 69 a
69 70 c
70 71 c
71 72 c
72 73 % convert again with --filemap
73 74 connecting to cvsrepo
74 75 scanning source...
75 76 using builtin cvsps
76 77 collecting CVS rlog
77 78 7 log entries
78 79 creating changesets
79 80 4 changeset entries
80 81 sorting...
81 82 converting...
82 83 0 ci1
83 84 c
84 85 c
85 86 c
86 87 3 ci1 files: b/c
87 88 2 update tags files: .hgtags
88 89 1 ci0 files: b/c
89 90 0 Initial revision files: b/c
90 91 % commit branch
91 92 U b/c
92 93 T a
93 94 T b/c
94 95 checking in src/b/c,v
95 96 % convert again
96 97 connecting to cvsrepo
97 98 scanning source...
98 99 using builtin cvsps
99 100 collecting CVS rlog
100 101 8 log entries
101 102 creating changesets
102 103 5 changeset entries
103 104 sorting...
104 105 converting...
105 106 0 ci2
106 107 a
107 108 c
108 109 d
109 110 % convert again with --filemap
110 111 connecting to cvsrepo
111 112 scanning source...
112 113 using builtin cvsps
113 114 collecting CVS rlog
114 115 8 log entries
115 116 creating changesets
116 117 5 changeset entries
117 118 sorting...
118 119 converting...
119 120 0 ci2
120 121 c
121 122 d
122 123 4 ci2 files: b/c
123 124 3 ci1 files: b/c
124 125 2 update tags files: .hgtags
125 126 1 ci0 files: b/c
126 127 0 Initial revision files: b/c
127 128 % commit a new revision with funny log message
128 129 checking in src/a,v
129 130 % convert again
130 131 connecting to cvsrepo
131 132 scanning source...
132 133 using builtin cvsps
133 134 collecting CVS rlog
134 135 9 log entries
135 136 creating changesets
136 137 6 changeset entries
137 138 sorting...
138 139 converting...
139 140 0 funny
140 141 o 6 (branch) funny
141 142 | ----------------------------
142 143 | log message files: a
143 144 o 5 (branch) ci2 files: b/c
144 145 |
145 146 | o 4 () ci1 files: a b/c
146 147 | |
147 148 | o 3 () update tags files: .hgtags
148 149 | |
149 150 | o 2 () ci0 files: b/c
150 151 |/
151 152 | o 1 (INITIAL) import files:
152 153 |/
153 154 o 0 () Initial revision files: a b/c
154 155
155 156 % testing debugcvsps
156 157 collecting CVS rlog
157 158 9 log entries
158 159 creating changesets
159 160 6 changeset entries
160 161 ---------------------
161 162 PatchSet 1
162 163 Date:
163 164 Author:
164 165 Branch: HEAD
165 166 Tag: (none)
166 167 Log:
167 168 Initial revision
168 169
169 170 Members:
170 171 a:INITIAL->1.1
171 172 b/c:INITIAL->1.1
172 173
173 174 ---------------------
174 175 PatchSet 2
175 176 Date:
176 177 Author:
177 178 Branch: INITIAL
178 179 Tag: start
179 180 Log:
180 181 import
181 182
182 183 Members:
183 184 a:1.1->1.1.1.1
184 185 b/c:1.1->1.1.1.1
185 186
186 187 ---------------------
187 188 PatchSet 3
188 189 Date:
189 190 Author:
190 191 Branch: HEAD
191 192 Tag: (none)
192 193 Log:
193 194 ci0
194 195
195 196 Members:
196 197 b/c:1.1->1.2
197 198
198 199 ---------------------
199 200 PatchSet 4
200 201 Date:
201 202 Author:
202 203 Branch: HEAD
203 204 Tag: (none)
204 205 Log:
205 206 ci1
206 207
207 208 Members:
208 209 a:1.1->1.2
209 210 b/c:1.2->1.3
210 211
211 212 ---------------------
212 213 PatchSet 5
213 214 Date:
214 215 Author:
215 216 Branch: branch
216 217 Tag: (none)
217 218 Log:
218 219 ci2
219 220
220 221 Members:
221 222 b/c:1.1->1.1.2.1
222 223
223 224 ---------------------
224 225 PatchSet 6
225 226 Date:
226 227 Author:
227 228 Branch: branch
228 229 Tag: (none)
229 230 Log:
230 231 funny
231 232 ----------------------------
232 233 log message
233 234
234 235 Members:
235 236 a:1.2->1.2.2.1
236 237
@@ -1,114 +1,115 b''
1 1 % create cvs repository
2 2 % create source directory
3 3 % import source directory
4 4 N src/a
5 5 N src/b/c
6 6
7 7 No conflicts created by this import
8 8
9 9 % checkout source directory
10 10 U src/a
11 11 U src/b/c
12 12 % commit a new revision changing b/c
13 13 checking in src/b/c,v
14 14 % convert fresh repo
15 15 warning: support for external cvsps is deprecated and will be removed in Mercurial 1.4
16 16 initializing destination src-hg repository
17 17 connecting to cvsrepo
18 18 scanning source...
19 19 sorting...
20 20 converting...
21 21 2 Initial revision
22 22 1 import
23 23 0 ci0
24 24 updating tags
25 25 a
26 26 c
27 27 c
28 28 % convert fresh repo with --filemap
29 29 warning: support for external cvsps is deprecated and will be removed in Mercurial 1.4
30 30 initializing destination src-filemap repository
31 31 connecting to cvsrepo
32 32 scanning source...
33 33 sorting...
34 34 converting...
35 35 2 Initial revision
36 36 1 import
37 filtering out empty revision
37 38 rolling back last transaction
38 39 0 ci0
39 40 updating tags
40 41 c
41 42 c
42 43 2 update tags files: .hgtags
43 44 1 ci0 files: b/c
44 45 0 Initial revision files: b/c
45 46 % commit new file revisions
46 47 checking in src/a,v
47 48 checking in src/b/c,v
48 49 % convert again
49 50 warning: support for external cvsps is deprecated and will be removed in Mercurial 1.4
50 51 connecting to cvsrepo
51 52 scanning source...
52 53 sorting...
53 54 converting...
54 55 0 ci1
55 56 a
56 57 a
57 58 c
58 59 c
59 60 c
60 61 % convert again with --filemap
61 62 warning: support for external cvsps is deprecated and will be removed in Mercurial 1.4
62 63 connecting to cvsrepo
63 64 scanning source...
64 65 sorting...
65 66 converting...
66 67 0 ci1
67 68 c
68 69 c
69 70 c
70 71 3 ci1 files: b/c
71 72 2 update tags files: .hgtags
72 73 1 ci0 files: b/c
73 74 0 Initial revision files: b/c
74 75 % commit branch
75 76 U b/c
76 77 T a
77 78 T b/c
78 79 checking in src/b/c,v
79 80 % convert again
80 81 warning: support for external cvsps is deprecated and will be removed in Mercurial 1.4
81 82 connecting to cvsrepo
82 83 scanning source...
83 84 sorting...
84 85 converting...
85 86 0 ci2
86 87 a
87 88 c
88 89 d
89 90 % convert again with --filemap
90 91 warning: support for external cvsps is deprecated and will be removed in Mercurial 1.4
91 92 connecting to cvsrepo
92 93 scanning source...
93 94 sorting...
94 95 converting...
95 96 0 ci2
96 97 c
97 98 d
98 99 4 ci2 files: b/c
99 100 3 ci1 files: b/c
100 101 2 update tags files: .hgtags
101 102 1 ci0 files: b/c
102 103 0 Initial revision files: b/c
103 104 o 5 (branch) ci2 files: b/c
104 105 |
105 106 | o 4 () ci1 files: a b/c
106 107 | |
107 108 | o 3 () update tags files: .hgtags
108 109 | |
109 110 | o 2 () ci0 files: b/c
110 111 |/
111 112 | o 1 (INITIAL) import files:
112 113 |/
113 114 o 0 () Initial revision files: a b/c
114 115
General Comments 0
You need to be logged in to leave comments. Login now