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(). |
|
|
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 |
|
|
|
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