##// END OF EJS Templates
convert: introduce --full for converting all files...
Mads Kiilerich -
r22300:35ab037d default
parent child Browse files
Show More
@@ -29,6 +29,8 testedwith = 'internal'
29 29 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
30 30 ('', 'filemap', '', _('remap file names using contents of file'),
31 31 _('FILE')),
32 ('', 'full', None,
33 _('apply filemap changes by converting all files again')),
32 34 ('', 'splicemap', '', _('splice synthesized history into place'),
33 35 _('FILE')),
34 36 ('', 'branchmap', '', _('change branch names while converting'),
@@ -131,6 +133,14 def convert(ui, src, dest=None, revmapfi
131 133 it is converted. To rename from a subdirectory into the root of
132 134 the repository, use ``.`` as the path to rename to.
133 135
136 ``--full`` will make sure the converted changesets contain exactly
137 the right files with the right content. It will make a full
138 conversion of all files, not just the ones that have
139 changed. Files that already are correct will not be changed. This
140 can be used to apply filemap changes when converting
141 incrementally. This is currently only supported for Mercurial and
142 Subversion.
143
134 144 The splicemap is a file that allows insertion of synthetic
135 145 history, letting you specify the parents of a revision. This is
136 146 useful if you want to e.g. give a Subversion merge two parents, or
@@ -134,8 +134,9 class bzr_source(converter_source):
134 134 sio = revtree.get_file(fileid)
135 135 return sio.read(), mode
136 136
137 def getchanges(self, version):
138 # set up caches: modecache and revtree
137 def getchanges(self, version, full):
138 if full:
139 raise util.Abort(_("convert from cvs do not support --full"))
139 140 self._modecache = {}
140 141 self._revtree = self.sourcerepo.revision_tree(version)
141 142 # get the parentids from the cache
@@ -93,12 +93,13 class converter_source(object):
93 93 """
94 94 raise NotImplementedError
95 95
96 def getchanges(self, version):
96 def getchanges(self, version, full):
97 97 """Returns a tuple of (files, copies).
98 98
99 99 files is a sorted list of (filename, id) tuples for all files
100 100 changed between version and its first parent returned by
101 getcommit(). id is the source revision id of the file.
101 getcommit(). If full, all files in that revision is returned.
102 id is the source revision id of the file.
102 103
103 104 copies is a dictionary of dest: source
104 105 """
@@ -204,7 +205,7 class converter_sink(object):
204 205 mapping equivalent authors identifiers for each system."""
205 206 return None
206 207
207 def putcommit(self, files, copies, parents, commit, source, revmap):
208 def putcommit(self, files, copies, parents, commit, source, revmap, full):
208 209 """Create a revision with all changed files listed in 'files'
209 210 and having listed parents. 'commit' is a commit object
210 211 containing at a minimum the author, date, and message for this
@@ -212,7 +213,8 class converter_sink(object):
212 213 'copies' is a dictionary mapping destinations to sources,
213 214 'source' is the source repository, and 'revmap' is a mapfile
214 215 of source revisions to converted revisions. Only getfile() and
215 lookuprev() should be called on 'source'.
216 lookuprev() should be called on 'source'. 'full' means that 'files'
217 is complete and all other files should be removed.
216 218
217 219 Note that the sink repository is not told to update itself to
218 220 a particular revision (or even what that revision would be)
@@ -386,8 +386,8 class converter(object):
386 386
387 387 def copy(self, rev):
388 388 commit = self.commitcache[rev]
389
390 changes = self.source.getchanges(rev)
389 full = self.opts.get('full')
390 changes = self.source.getchanges(rev, full)
391 391 if isinstance(changes, basestring):
392 392 if changes == SKIPREV:
393 393 dest = SKIPREV
@@ -413,7 +413,7 class converter(object):
413 413 parents = [b[0] for b in pbranches]
414 414 source = progresssource(self.ui, self.source, len(files))
415 415 newnode = self.dest.putcommit(files, copies, parents, commit,
416 source, self.map)
416 source, self.map, full)
417 417 source.close()
418 418 self.source.converted(rev, newnode)
419 419 self.map[rev] = newnode
@@ -258,7 +258,9 class convert_cvs(converter_source):
258 258 else:
259 259 raise util.Abort(_("unknown CVS response: %s") % line)
260 260
261 def getchanges(self, rev):
261 def getchanges(self, rev, full):
262 if full:
263 raise util.Abort(_("convert from cvs do not support --full"))
262 264 self._parse()
263 265 return sorted(self.files[rev].iteritems()), {}
264 266
@@ -156,7 +156,9 class darcs_source(converter_source, com
156 156 output, status = self.run('revert', all=True, repodir=self.tmppath)
157 157 self.checkexit(status, output)
158 158
159 def getchanges(self, rev):
159 def getchanges(self, rev, full):
160 if full:
161 raise util.Abort(_("convert from darcs do not support --full"))
160 162 copies = {}
161 163 changes = []
162 164 man = None
@@ -304,7 +304,7 class filemap_source(converter_source):
304 304 wrev.add(rev)
305 305 self.wantedancestors[rev] = wrev
306 306
307 def getchanges(self, rev):
307 def getchanges(self, rev, full):
308 308 parents = self.commits[rev].parents
309 309 if len(parents) > 1:
310 310 self.rebuild()
@@ -384,7 +384,7 class filemap_source(converter_source):
384 384 # Get the real changes and do the filtering/mapping. To be
385 385 # able to get the files later on in getfile, we hide the
386 386 # original filename in the rev part of the return value.
387 changes, copies = self.base.getchanges(rev)
387 changes, copies = self.base.getchanges(rev, full)
388 388 files = {}
389 389 for f, r in changes:
390 390 newf = self.filemapper(f)
@@ -180,7 +180,9 class convert_git(converter_source):
180 180 continue
181 181 m.node = node.strip()
182 182
183 def getchanges(self, version):
183 def getchanges(self, version, full):
184 if full:
185 raise util.Abort(_("convert from git do not support --full"))
184 186 self.modecache = {}
185 187 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
186 188 changes = []
@@ -142,7 +142,9 class gnuarch_source(converter_source, c
142 142
143 143 return self._getfile(name, rev)
144 144
145 def getchanges(self, rev):
145 def getchanges(self, rev, full):
146 if full:
147 raise util.Abort(_("convert from arch do not support --full"))
146 148 self._update(rev)
147 149 changes = []
148 150 copies = {}
@@ -128,11 +128,13 class mercurial_sink(converter_sink):
128 128 fp.write('%s %s\n' % (revid, s[1]))
129 129 return fp.getvalue()
130 130
131 def putcommit(self, files, copies, parents, commit, source, revmap):
132
131 def putcommit(self, files, copies, parents, commit, source, revmap, full):
133 132 files = dict(files)
134 133 def getfilectx(repo, memctx, f):
134 try:
135 135 v = files[f]
136 except KeyError:
137 return None
136 138 data, mode = source.getfile(f, v)
137 139 if data is None:
138 140 return None
@@ -193,7 +195,10 class mercurial_sink(converter_sink):
193 195 while parents:
194 196 p1 = p2
195 197 p2 = parents.pop(0)
196 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
198 fileset = set(files)
199 if full:
200 fileset.update(self.repo[p1], self.repo[p2])
201 ctx = context.memctx(self.repo, (p1, p2), text, fileset,
197 202 getfilectx, commit.author, commit.date, extra)
198 203 self.repo.commitctx(ctx)
199 204 text = "(octopus merge fixup)\n"
@@ -356,16 +361,17 class mercurial_source(converter_source)
356 361 except error.LookupError:
357 362 return None, None
358 363
359 def getchanges(self, rev):
364 def getchanges(self, rev, full):
360 365 ctx = self.changectx(rev)
361 366 parents = self.parents(ctx)
362 if not parents:
367 if full or not parents:
363 368 files = copyfiles = ctx.manifest()
364 else:
369 if parents:
365 370 if self._changescache[0] == rev:
366 371 m, a, r = self._changescache[1]
367 372 else:
368 373 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
374 if not full:
369 375 files = m + a + r
370 376 copyfiles = m + a
371 377 # getcopies() is also run for roots and before filtering so missing
@@ -224,7 +224,9 class monotone_source(converter_source,
224 224 else:
225 225 return [self.rev]
226 226
227 def getchanges(self, rev):
227 def getchanges(self, rev, full):
228 if full:
229 raise util.Abort(_("convert from monotone do not support --full"))
228 230 revision = self.mtnrun("get_revision", rev).split("\n\n")
229 231 files = {}
230 232 ignoremove = {}
@@ -192,7 +192,9 class p4_source(converter_source):
192 192
193 193 return contents, mode
194 194
195 def getchanges(self, rev):
195 def getchanges(self, rev, full):
196 if full:
197 raise util.Abort(_("convert from p4 do not support --full"))
196 198 return self.files[rev], {}
197 199
198 200 def getcommit(self, rev):
@@ -444,37 +444,37 class svn_source(converter_source):
444 444
445 445 return self.heads
446 446
447 def _getchanges(self, rev):
447 def _getchanges(self, rev, full):
448 448 (paths, parents) = self.paths[rev]
449 copies = {}
449 450 if parents:
450 451 files, self.removed, copies = self.expandpaths(rev, paths, parents)
451 else:
452 if full or not parents:
452 453 # Perform a full checkout on roots
453 454 uuid, module, revnum = revsplit(rev)
454 455 entries = svn.client.ls(self.baseurl + quote(module),
455 456 optrev(revnum), True, self.ctx)
456 457 files = [n for n, e in entries.iteritems()
457 458 if e.kind == svn.core.svn_node_file]
458 copies = {}
459 459 self.removed = set()
460 460
461 461 files.sort()
462 462 files = zip(files, [rev] * len(files))
463 463 return (files, copies)
464 464
465 def getchanges(self, rev):
465 def getchanges(self, rev, full):
466 466 # reuse cache from getchangedfiles
467 if self._changescache[0] == rev:
467 if self._changescache[0] == rev and not full:
468 468 (files, copies) = self._changescache[1]
469 469 else:
470 (files, copies) = self._getchanges(rev)
470 (files, copies) = self._getchanges(rev, full)
471 471 # caller caches the result, so free it here to release memory
472 472 del self.paths[rev]
473 473 return (files, copies)
474 474
475 475 def getchangedfiles(self, rev, i):
476 476 # called from filemap - cache computed values for reuse in getchanges
477 (files, copies) = self._getchanges(rev)
477 (files, copies) = self._getchanges(rev, False)
478 478 self._changescache = (rev, (files, copies))
479 479 return [f[0] for f in files]
480 480
@@ -1222,7 +1222,7 class svn_sink(converter_sink, commandli
1222 1222 def revid(self, rev):
1223 1223 return u"svn:%s@%s" % (self.uuid, rev)
1224 1224
1225 def putcommit(self, files, copies, parents, commit, source, revmap):
1225 def putcommit(self, files, copies, parents, commit, source, revmap, full):
1226 1226 for parent in parents:
1227 1227 try:
1228 1228 return self.revid(self.childmap[parent])
@@ -1238,6 +1238,8 class svn_sink(converter_sink, commandli
1238 1238 self.putfile(f, mode, data)
1239 1239 if f in copies:
1240 1240 self.copies.append([copies[f], f])
1241 if full:
1242 self.delete.extend(sorted(self.manifest.difference(files)))
1241 1243 files = [f[0] for f in files]
1242 1244
1243 1245 entries = set(self.delete)
@@ -537,3 +537,16 Conversion after rollback
537 537 |
538 538 o 0 0 (a-only f)
539 539
540 Convert with --full adds and removes files that didn't change
541
542 $ echo f >> 0/f
543 $ hg -R 0 ci -m "f"
544 $ hg convert --filemap filemap-b --full 0 a --config convert.hg.revs=1::
545 scanning source...
546 sorting...
547 converting...
548 0 f
549 $ hg -R a status --change tip
550 M f
551 A b-only
552 R a-only
@@ -247,6 +247,31 Symlinks
247 247
248 248 #endif
249 249
250 Convert with --full adds and removes files that didn't change
251
252 $ touch a/f
253 $ hg -R a ci -Aqmf
254 $ echo "rename c d" > filemap
255 $ hg convert -d svn a --filemap filemap --full
256 assuming destination a-hg
257 initializing svn working copy 'a-hg-wc'
258 scanning source...
259 sorting...
260 converting...
261 0 f
262 $ svnupanddisplay a-hg-wc 1
263 9 9 test .
264 9 9 test d
265 9 9 test f
266 revision: 9
267 author: test
268 msg: f
269 D /c
270 A /d
271 D /d1
272 A /f
273 D /newlink
274
250 275 $ rm -rf a a-hg a-hg-wc
251 276
252 277
@@ -168,6 +168,27 Test filemap
168 168 |
169 169 o 0 second letter files: letter2.txt
170 170
171 Convert with --full adds and removes files that didn't change
172
173 $ cd B
174 $ echo >> "letter .txt"
175 $ svn ci -m 'nothing'
176 Sending letter .txt
177 Transmitting file data .
178 Committed revision 9.
179 $ cd ..
180
181 $ echo 'rename letter2.txt letter3.txt' > filemap
182 $ hg convert --filemap filemap --full "$SVNREPOURL/proj%20B/mytrunk" fmap
183 scanning source...
184 sorting...
185 converting...
186 0 nothing
187 $ hg -R fmap st --change tip
188 A letter .txt
189 A letter3.txt
190 R letter2.txt
191
171 192 test invalid splicemap1
172 193
173 194 $ cat > splicemap <<EOF
@@ -91,6 +91,13
91 91 directory if it is converted. To rename from a subdirectory into the root
92 92 of the repository, use "." as the path to rename to.
93 93
94 "--full" will make sure the converted changesets contain exactly the right
95 files with the right content. It will make a full conversion of all files,
96 not just the ones that have changed. Files that already are correct will
97 not be changed. This can be used to apply filemap changes when converting
98 incrementally. This is currently only supported for Mercurial and
99 Subversion.
100
94 101 The splicemap is a file that allows insertion of synthetic history,
95 102 letting you specify the parents of a revision. This is useful if you want
96 103 to e.g. give a Subversion merge two parents, or graft two disconnected
@@ -265,6 +272,7
265 272 -r --rev REV import up to source revision REV
266 273 -A --authormap FILE remap usernames using this file
267 274 --filemap FILE remap file names using contents of file
275 --full apply filemap changes by converting all files again
268 276 --splicemap FILE splice synthesized history into place
269 277 --branchmap FILE change branch names while converting
270 278 --branchsort try to sort changesets by branches
General Comments 0
You need to be logged in to leave comments. Login now