##// 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 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
29 ('A', 'authormap', '', _('remap usernames using this file'), _('FILE')),
30 ('', 'filemap', '', _('remap file names using contents of file'),
30 ('', 'filemap', '', _('remap file names using contents of file'),
31 _('FILE')),
31 _('FILE')),
32 ('', 'full', None,
33 _('apply filemap changes by converting all files again')),
32 ('', 'splicemap', '', _('splice synthesized history into place'),
34 ('', 'splicemap', '', _('splice synthesized history into place'),
33 _('FILE')),
35 _('FILE')),
34 ('', 'branchmap', '', _('change branch names while converting'),
36 ('', 'branchmap', '', _('change branch names while converting'),
@@ -131,6 +133,14 def convert(ui, src, dest=None, revmapfi
131 it is converted. To rename from a subdirectory into the root of
133 it is converted. To rename from a subdirectory into the root of
132 the repository, use ``.`` as the path to rename to.
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 The splicemap is a file that allows insertion of synthetic
144 The splicemap is a file that allows insertion of synthetic
135 history, letting you specify the parents of a revision. This is
145 history, letting you specify the parents of a revision. This is
136 useful if you want to e.g. give a Subversion merge two parents, or
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 sio = revtree.get_file(fileid)
134 sio = revtree.get_file(fileid)
135 return sio.read(), mode
135 return sio.read(), mode
136
136
137 def getchanges(self, version):
137 def getchanges(self, version, full):
138 # set up caches: modecache and revtree
138 if full:
139 raise util.Abort(_("convert from cvs do not support --full"))
139 self._modecache = {}
140 self._modecache = {}
140 self._revtree = self.sourcerepo.revision_tree(version)
141 self._revtree = self.sourcerepo.revision_tree(version)
141 # get the parentids from the cache
142 # get the parentids from the cache
@@ -93,12 +93,13 class converter_source(object):
93 """
93 """
94 raise NotImplementedError
94 raise NotImplementedError
95
95
96 def getchanges(self, version):
96 def getchanges(self, version, full):
97 """Returns a tuple of (files, copies).
97 """Returns a tuple of (files, copies).
98
98
99 files is a sorted list of (filename, id) tuples for all files
99 files is a sorted list of (filename, id) tuples for all files
100 changed between version and its first parent returned by
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 copies is a dictionary of dest: source
104 copies is a dictionary of dest: source
104 """
105 """
@@ -204,7 +205,7 class converter_sink(object):
204 mapping equivalent authors identifiers for each system."""
205 mapping equivalent authors identifiers for each system."""
205 return None
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 """Create a revision with all changed files listed in 'files'
209 """Create a revision with all changed files listed in 'files'
209 and having listed parents. 'commit' is a commit object
210 and having listed parents. 'commit' is a commit object
210 containing at a minimum the author, date, and message for this
211 containing at a minimum the author, date, and message for this
@@ -212,7 +213,8 class converter_sink(object):
212 'copies' is a dictionary mapping destinations to sources,
213 'copies' is a dictionary mapping destinations to sources,
213 'source' is the source repository, and 'revmap' is a mapfile
214 'source' is the source repository, and 'revmap' is a mapfile
214 of source revisions to converted revisions. Only getfile() and
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 Note that the sink repository is not told to update itself to
219 Note that the sink repository is not told to update itself to
218 a particular revision (or even what that revision would be)
220 a particular revision (or even what that revision would be)
@@ -386,8 +386,8 class converter(object):
386
386
387 def copy(self, rev):
387 def copy(self, rev):
388 commit = self.commitcache[rev]
388 commit = self.commitcache[rev]
389
389 full = self.opts.get('full')
390 changes = self.source.getchanges(rev)
390 changes = self.source.getchanges(rev, full)
391 if isinstance(changes, basestring):
391 if isinstance(changes, basestring):
392 if changes == SKIPREV:
392 if changes == SKIPREV:
393 dest = SKIPREV
393 dest = SKIPREV
@@ -413,7 +413,7 class converter(object):
413 parents = [b[0] for b in pbranches]
413 parents = [b[0] for b in pbranches]
414 source = progresssource(self.ui, self.source, len(files))
414 source = progresssource(self.ui, self.source, len(files))
415 newnode = self.dest.putcommit(files, copies, parents, commit,
415 newnode = self.dest.putcommit(files, copies, parents, commit,
416 source, self.map)
416 source, self.map, full)
417 source.close()
417 source.close()
418 self.source.converted(rev, newnode)
418 self.source.converted(rev, newnode)
419 self.map[rev] = newnode
419 self.map[rev] = newnode
@@ -258,7 +258,9 class convert_cvs(converter_source):
258 else:
258 else:
259 raise util.Abort(_("unknown CVS response: %s") % line)
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 self._parse()
264 self._parse()
263 return sorted(self.files[rev].iteritems()), {}
265 return sorted(self.files[rev].iteritems()), {}
264
266
@@ -156,7 +156,9 class darcs_source(converter_source, com
156 output, status = self.run('revert', all=True, repodir=self.tmppath)
156 output, status = self.run('revert', all=True, repodir=self.tmppath)
157 self.checkexit(status, output)
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 copies = {}
162 copies = {}
161 changes = []
163 changes = []
162 man = None
164 man = None
@@ -304,7 +304,7 class filemap_source(converter_source):
304 wrev.add(rev)
304 wrev.add(rev)
305 self.wantedancestors[rev] = wrev
305 self.wantedancestors[rev] = wrev
306
306
307 def getchanges(self, rev):
307 def getchanges(self, rev, full):
308 parents = self.commits[rev].parents
308 parents = self.commits[rev].parents
309 if len(parents) > 1:
309 if len(parents) > 1:
310 self.rebuild()
310 self.rebuild()
@@ -384,7 +384,7 class filemap_source(converter_source):
384 # Get the real changes and do the filtering/mapping. To be
384 # Get the real changes and do the filtering/mapping. To be
385 # able to get the files later on in getfile, we hide the
385 # able to get the files later on in getfile, we hide the
386 # original filename in the rev part of the return value.
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 files = {}
388 files = {}
389 for f, r in changes:
389 for f, r in changes:
390 newf = self.filemapper(f)
390 newf = self.filemapper(f)
@@ -180,7 +180,9 class convert_git(converter_source):
180 continue
180 continue
181 m.node = node.strip()
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 self.modecache = {}
186 self.modecache = {}
185 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
187 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
186 changes = []
188 changes = []
@@ -142,7 +142,9 class gnuarch_source(converter_source, c
142
142
143 return self._getfile(name, rev)
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 self._update(rev)
148 self._update(rev)
147 changes = []
149 changes = []
148 copies = {}
150 copies = {}
@@ -128,11 +128,13 class mercurial_sink(converter_sink):
128 fp.write('%s %s\n' % (revid, s[1]))
128 fp.write('%s %s\n' % (revid, s[1]))
129 return fp.getvalue()
129 return fp.getvalue()
130
130
131 def putcommit(self, files, copies, parents, commit, source, revmap):
131 def putcommit(self, files, copies, parents, commit, source, revmap, full):
132
133 files = dict(files)
132 files = dict(files)
134 def getfilectx(repo, memctx, f):
133 def getfilectx(repo, memctx, f):
134 try:
135 v = files[f]
135 v = files[f]
136 except KeyError:
137 return None
136 data, mode = source.getfile(f, v)
138 data, mode = source.getfile(f, v)
137 if data is None:
139 if data is None:
138 return None
140 return None
@@ -193,7 +195,10 class mercurial_sink(converter_sink):
193 while parents:
195 while parents:
194 p1 = p2
196 p1 = p2
195 p2 = parents.pop(0)
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 getfilectx, commit.author, commit.date, extra)
202 getfilectx, commit.author, commit.date, extra)
198 self.repo.commitctx(ctx)
203 self.repo.commitctx(ctx)
199 text = "(octopus merge fixup)\n"
204 text = "(octopus merge fixup)\n"
@@ -356,16 +361,17 class mercurial_source(converter_source)
356 except error.LookupError:
361 except error.LookupError:
357 return None, None
362 return None, None
358
363
359 def getchanges(self, rev):
364 def getchanges(self, rev, full):
360 ctx = self.changectx(rev)
365 ctx = self.changectx(rev)
361 parents = self.parents(ctx)
366 parents = self.parents(ctx)
362 if not parents:
367 if full or not parents:
363 files = copyfiles = ctx.manifest()
368 files = copyfiles = ctx.manifest()
364 else:
369 if parents:
365 if self._changescache[0] == rev:
370 if self._changescache[0] == rev:
366 m, a, r = self._changescache[1]
371 m, a, r = self._changescache[1]
367 else:
372 else:
368 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
373 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
374 if not full:
369 files = m + a + r
375 files = m + a + r
370 copyfiles = m + a
376 copyfiles = m + a
371 # getcopies() is also run for roots and before filtering so missing
377 # getcopies() is also run for roots and before filtering so missing
@@ -224,7 +224,9 class monotone_source(converter_source,
224 else:
224 else:
225 return [self.rev]
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 revision = self.mtnrun("get_revision", rev).split("\n\n")
230 revision = self.mtnrun("get_revision", rev).split("\n\n")
229 files = {}
231 files = {}
230 ignoremove = {}
232 ignoremove = {}
@@ -192,7 +192,9 class p4_source(converter_source):
192
192
193 return contents, mode
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 return self.files[rev], {}
198 return self.files[rev], {}
197
199
198 def getcommit(self, rev):
200 def getcommit(self, rev):
@@ -444,37 +444,37 class svn_source(converter_source):
444
444
445 return self.heads
445 return self.heads
446
446
447 def _getchanges(self, rev):
447 def _getchanges(self, rev, full):
448 (paths, parents) = self.paths[rev]
448 (paths, parents) = self.paths[rev]
449 copies = {}
449 if parents:
450 if parents:
450 files, self.removed, copies = self.expandpaths(rev, paths, parents)
451 files, self.removed, copies = self.expandpaths(rev, paths, parents)
451 else:
452 if full or not parents:
452 # Perform a full checkout on roots
453 # Perform a full checkout on roots
453 uuid, module, revnum = revsplit(rev)
454 uuid, module, revnum = revsplit(rev)
454 entries = svn.client.ls(self.baseurl + quote(module),
455 entries = svn.client.ls(self.baseurl + quote(module),
455 optrev(revnum), True, self.ctx)
456 optrev(revnum), True, self.ctx)
456 files = [n for n, e in entries.iteritems()
457 files = [n for n, e in entries.iteritems()
457 if e.kind == svn.core.svn_node_file]
458 if e.kind == svn.core.svn_node_file]
458 copies = {}
459 self.removed = set()
459 self.removed = set()
460
460
461 files.sort()
461 files.sort()
462 files = zip(files, [rev] * len(files))
462 files = zip(files, [rev] * len(files))
463 return (files, copies)
463 return (files, copies)
464
464
465 def getchanges(self, rev):
465 def getchanges(self, rev, full):
466 # reuse cache from getchangedfiles
466 # reuse cache from getchangedfiles
467 if self._changescache[0] == rev:
467 if self._changescache[0] == rev and not full:
468 (files, copies) = self._changescache[1]
468 (files, copies) = self._changescache[1]
469 else:
469 else:
470 (files, copies) = self._getchanges(rev)
470 (files, copies) = self._getchanges(rev, full)
471 # caller caches the result, so free it here to release memory
471 # caller caches the result, so free it here to release memory
472 del self.paths[rev]
472 del self.paths[rev]
473 return (files, copies)
473 return (files, copies)
474
474
475 def getchangedfiles(self, rev, i):
475 def getchangedfiles(self, rev, i):
476 # called from filemap - cache computed values for reuse in getchanges
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 self._changescache = (rev, (files, copies))
478 self._changescache = (rev, (files, copies))
479 return [f[0] for f in files]
479 return [f[0] for f in files]
480
480
@@ -1222,7 +1222,7 class svn_sink(converter_sink, commandli
1222 def revid(self, rev):
1222 def revid(self, rev):
1223 return u"svn:%s@%s" % (self.uuid, rev)
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 for parent in parents:
1226 for parent in parents:
1227 try:
1227 try:
1228 return self.revid(self.childmap[parent])
1228 return self.revid(self.childmap[parent])
@@ -1238,6 +1238,8 class svn_sink(converter_sink, commandli
1238 self.putfile(f, mode, data)
1238 self.putfile(f, mode, data)
1239 if f in copies:
1239 if f in copies:
1240 self.copies.append([copies[f], f])
1240 self.copies.append([copies[f], f])
1241 if full:
1242 self.delete.extend(sorted(self.manifest.difference(files)))
1241 files = [f[0] for f in files]
1243 files = [f[0] for f in files]
1242
1244
1243 entries = set(self.delete)
1245 entries = set(self.delete)
@@ -537,3 +537,16 Conversion after rollback
537 |
537 |
538 o 0 0 (a-only f)
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 #endif
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 $ rm -rf a a-hg a-hg-wc
275 $ rm -rf a a-hg a-hg-wc
251
276
252
277
@@ -168,6 +168,27 Test filemap
168 |
168 |
169 o 0 second letter files: letter2.txt
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 test invalid splicemap1
192 test invalid splicemap1
172
193
173 $ cat > splicemap <<EOF
194 $ cat > splicemap <<EOF
@@ -91,6 +91,13
91 directory if it is converted. To rename from a subdirectory into the root
91 directory if it is converted. To rename from a subdirectory into the root
92 of the repository, use "." as the path to rename to.
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 The splicemap is a file that allows insertion of synthetic history,
101 The splicemap is a file that allows insertion of synthetic history,
95 letting you specify the parents of a revision. This is useful if you want
102 letting you specify the parents of a revision. This is useful if you want
96 to e.g. give a Subversion merge two parents, or graft two disconnected
103 to e.g. give a Subversion merge two parents, or graft two disconnected
@@ -265,6 +272,7
265 -r --rev REV import up to source revision REV
272 -r --rev REV import up to source revision REV
266 -A --authormap FILE remap usernames using this file
273 -A --authormap FILE remap usernames using this file
267 --filemap FILE remap file names using contents of file
274 --filemap FILE remap file names using contents of file
275 --full apply filemap changes by converting all files again
268 --splicemap FILE splice synthesized history into place
276 --splicemap FILE splice synthesized history into place
269 --branchmap FILE change branch names while converting
277 --branchmap FILE change branch names while converting
270 --branchsort try to sort changesets by branches
278 --branchsort try to sort changesets by branches
General Comments 0
You need to be logged in to leave comments. Login now