##// END OF EJS Templates
convert: fix crash when existing converted revision didn't come from source...
Mads Kiilerich -
r19863:daeab82f default
parent child Browse files
Show More
@@ -1,404 +1,411
1 1 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
2 2 # Copyright 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
3 3 #
4 4 # This software may be used and distributed according to the terms of the
5 5 # GNU General Public License version 2 or any later version.
6 6
7 7 import posixpath
8 8 import shlex
9 9 from mercurial.i18n import _
10 from mercurial import util
10 from mercurial import util, error
11 11 from common import SKIPREV, converter_source
12 12
13 13 def rpairs(name):
14 14 e = len(name)
15 15 while e != -1:
16 16 yield name[:e], name[e + 1:]
17 17 e = name.rfind('/', 0, e)
18 18 yield '.', name
19 19
20 20 def normalize(path):
21 21 ''' We use posixpath.normpath to support cross-platform path format.
22 22 However, it doesn't handle None input. So we wrap it up. '''
23 23 if path is None:
24 24 return None
25 25 return posixpath.normpath(path)
26 26
27 27 class filemapper(object):
28 28 '''Map and filter filenames when importing.
29 29 A name can be mapped to itself, a new name, or None (omit from new
30 30 repository).'''
31 31
32 32 def __init__(self, ui, path=None):
33 33 self.ui = ui
34 34 self.include = {}
35 35 self.exclude = {}
36 36 self.rename = {}
37 37 if path:
38 38 if self.parse(path):
39 39 raise util.Abort(_('errors in filemap'))
40 40
41 41 def parse(self, path):
42 42 errs = 0
43 43 def check(name, mapping, listname):
44 44 if not name:
45 45 self.ui.warn(_('%s:%d: path to %s is missing\n') %
46 46 (lex.infile, lex.lineno, listname))
47 47 return 1
48 48 if name in mapping:
49 49 self.ui.warn(_('%s:%d: %r already in %s list\n') %
50 50 (lex.infile, lex.lineno, name, listname))
51 51 return 1
52 52 if (name.startswith('/') or
53 53 name.endswith('/') or
54 54 '//' in name):
55 55 self.ui.warn(_('%s:%d: superfluous / in %s %r\n') %
56 56 (lex.infile, lex.lineno, listname, name))
57 57 return 1
58 58 return 0
59 59 lex = shlex.shlex(open(path), path, True)
60 60 lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?'
61 61 cmd = lex.get_token()
62 62 while cmd:
63 63 if cmd == 'include':
64 64 name = normalize(lex.get_token())
65 65 errs += check(name, self.exclude, 'exclude')
66 66 self.include[name] = name
67 67 elif cmd == 'exclude':
68 68 name = normalize(lex.get_token())
69 69 errs += check(name, self.include, 'include')
70 70 errs += check(name, self.rename, 'rename')
71 71 self.exclude[name] = name
72 72 elif cmd == 'rename':
73 73 src = normalize(lex.get_token())
74 74 dest = normalize(lex.get_token())
75 75 errs += check(src, self.exclude, 'exclude')
76 76 self.rename[src] = dest
77 77 elif cmd == 'source':
78 78 errs += self.parse(normalize(lex.get_token()))
79 79 else:
80 80 self.ui.warn(_('%s:%d: unknown directive %r\n') %
81 81 (lex.infile, lex.lineno, cmd))
82 82 errs += 1
83 83 cmd = lex.get_token()
84 84 return errs
85 85
86 86 def lookup(self, name, mapping):
87 87 name = normalize(name)
88 88 for pre, suf in rpairs(name):
89 89 try:
90 90 return mapping[pre], pre, suf
91 91 except KeyError:
92 92 pass
93 93 return '', name, ''
94 94
95 95 def __call__(self, name):
96 96 if self.include:
97 97 inc = self.lookup(name, self.include)[0]
98 98 else:
99 99 inc = name
100 100 if self.exclude:
101 101 exc = self.lookup(name, self.exclude)[0]
102 102 else:
103 103 exc = ''
104 104 if (not self.include and exc) or (len(inc) <= len(exc)):
105 105 return None
106 106 newpre, pre, suf = self.lookup(name, self.rename)
107 107 if newpre:
108 108 if newpre == '.':
109 109 return suf
110 110 if suf:
111 111 if newpre.endswith('/'):
112 112 return newpre + suf
113 113 return newpre + '/' + suf
114 114 return newpre
115 115 return name
116 116
117 117 def active(self):
118 118 return bool(self.include or self.exclude or self.rename)
119 119
120 120 # This class does two additional things compared to a regular source:
121 121 #
122 122 # - Filter and rename files. This is mostly wrapped by the filemapper
123 123 # class above. We hide the original filename in the revision that is
124 124 # returned by getchanges to be able to find things later in getfile.
125 125 #
126 126 # - Return only revisions that matter for the files we're interested in.
127 127 # This involves rewriting the parents of the original revision to
128 128 # create a graph that is restricted to those revisions.
129 129 #
130 130 # This set of revisions includes not only revisions that directly
131 131 # touch files we're interested in, but also merges that merge two
132 132 # or more interesting revisions.
133 133
134 134 class filemap_source(converter_source):
135 135 def __init__(self, ui, baseconverter, filemap):
136 136 super(filemap_source, self).__init__(ui)
137 137 self.base = baseconverter
138 138 self.filemapper = filemapper(ui, filemap)
139 139 self.commits = {}
140 140 # if a revision rev has parent p in the original revision graph, then
141 141 # rev will have parent self.parentmap[p] in the restricted graph.
142 142 self.parentmap = {}
143 143 # self.wantedancestors[rev] is the set of all ancestors of rev that
144 144 # are in the restricted graph.
145 145 self.wantedancestors = {}
146 146 self.convertedorder = None
147 147 self._rebuilt = False
148 148 self.origparents = {}
149 149 self.children = {}
150 150 self.seenchildren = {}
151 151
152 152 def before(self):
153 153 self.base.before()
154 154
155 155 def after(self):
156 156 self.base.after()
157 157
158 158 def setrevmap(self, revmap):
159 159 # rebuild our state to make things restartable
160 160 #
161 161 # To avoid calling getcommit for every revision that has already
162 162 # been converted, we rebuild only the parentmap, delaying the
163 163 # rebuild of wantedancestors until we need it (i.e. until a
164 164 # merge).
165 165 #
166 166 # We assume the order argument lists the revisions in
167 167 # topological order, so that we can infer which revisions were
168 168 # wanted by previous runs.
169 169 self._rebuilt = not revmap
170 170 seen = {SKIPREV: SKIPREV}
171 171 dummyset = set()
172 172 converted = []
173 173 for rev in revmap.order:
174 174 mapped = revmap[rev]
175 175 wanted = mapped not in seen
176 176 if wanted:
177 177 seen[mapped] = rev
178 178 self.parentmap[rev] = rev
179 179 else:
180 180 self.parentmap[rev] = seen[mapped]
181 181 self.wantedancestors[rev] = dummyset
182 182 arg = seen[mapped]
183 183 if arg == SKIPREV:
184 184 arg = None
185 185 converted.append((rev, wanted, arg))
186 186 self.convertedorder = converted
187 187 return self.base.setrevmap(revmap)
188 188
189 189 def rebuild(self):
190 190 if self._rebuilt:
191 191 return True
192 192 self._rebuilt = True
193 193 self.parentmap.clear()
194 194 self.wantedancestors.clear()
195 195 self.seenchildren.clear()
196 196 for rev, wanted, arg in self.convertedorder:
197 197 if rev not in self.origparents:
198 try:
198 199 self.origparents[rev] = self.getcommit(rev).parents
200 except error.RepoLookupError:
201 self.ui.debug("unknown revmap source: %s\n" % rev)
202 continue
199 203 if arg is not None:
200 204 self.children[arg] = self.children.get(arg, 0) + 1
201 205
202 206 for rev, wanted, arg in self.convertedorder:
207 try:
203 208 parents = self.origparents[rev]
209 except KeyError:
210 continue # unknown revmap source
204 211 if wanted:
205 212 self.mark_wanted(rev, parents)
206 213 else:
207 214 self.mark_not_wanted(rev, arg)
208 215 self._discard(arg, *parents)
209 216
210 217 return True
211 218
212 219 def getheads(self):
213 220 return self.base.getheads()
214 221
215 222 def getcommit(self, rev):
216 223 # We want to save a reference to the commit objects to be able
217 224 # to rewrite their parents later on.
218 225 c = self.commits[rev] = self.base.getcommit(rev)
219 226 for p in c.parents:
220 227 self.children[p] = self.children.get(p, 0) + 1
221 228 return c
222 229
223 230 def _cachedcommit(self, rev):
224 231 if rev in self.commits:
225 232 return self.commits[rev]
226 233 return self.base.getcommit(rev)
227 234
228 235 def _discard(self, *revs):
229 236 for r in revs:
230 237 if r is None:
231 238 continue
232 239 self.seenchildren[r] = self.seenchildren.get(r, 0) + 1
233 240 if self.seenchildren[r] == self.children[r]:
234 241 self.wantedancestors.pop(r, None)
235 242 self.parentmap.pop(r, None)
236 243 del self.seenchildren[r]
237 244 if self._rebuilt:
238 245 del self.children[r]
239 246
240 247 def wanted(self, rev, i):
241 248 # Return True if we're directly interested in rev.
242 249 #
243 250 # i is an index selecting one of the parents of rev (if rev
244 251 # has no parents, i is None). getchangedfiles will give us
245 252 # the list of files that are different in rev and in the parent
246 253 # indicated by i. If we're interested in any of these files,
247 254 # we're interested in rev.
248 255 try:
249 256 files = self.base.getchangedfiles(rev, i)
250 257 except NotImplementedError:
251 258 raise util.Abort(_("source repository doesn't support --filemap"))
252 259 for f in files:
253 260 if self.filemapper(f):
254 261 return True
255 262 return False
256 263
257 264 def mark_not_wanted(self, rev, p):
258 265 # Mark rev as not interesting and update data structures.
259 266
260 267 if p is None:
261 268 # A root revision. Use SKIPREV to indicate that it doesn't
262 269 # map to any revision in the restricted graph. Put SKIPREV
263 270 # in the set of wanted ancestors to simplify code elsewhere
264 271 self.parentmap[rev] = SKIPREV
265 272 self.wantedancestors[rev] = set((SKIPREV,))
266 273 return
267 274
268 275 # Reuse the data from our parent.
269 276 self.parentmap[rev] = self.parentmap[p]
270 277 self.wantedancestors[rev] = self.wantedancestors[p]
271 278
272 279 def mark_wanted(self, rev, parents):
273 280 # Mark rev ss wanted and update data structures.
274 281
275 282 # rev will be in the restricted graph, so children of rev in
276 283 # the original graph should still have rev as a parent in the
277 284 # restricted graph.
278 285 self.parentmap[rev] = rev
279 286
280 287 # The set of wanted ancestors of rev is the union of the sets
281 288 # of wanted ancestors of its parents. Plus rev itself.
282 289 wrev = set()
283 290 for p in parents:
284 291 if p in self.wantedancestors:
285 292 wrev.update(self.wantedancestors[p])
286 293 else:
287 294 self.ui.warn(_('warning: %s parent %s is missing\n') %
288 295 (rev, p))
289 296 wrev.add(rev)
290 297 self.wantedancestors[rev] = wrev
291 298
292 299 def getchanges(self, rev):
293 300 parents = self.commits[rev].parents
294 301 if len(parents) > 1:
295 302 self.rebuild()
296 303
297 304 # To decide whether we're interested in rev we:
298 305 #
299 306 # - calculate what parents rev will have if it turns out we're
300 307 # interested in it. If it's going to have more than 1 parent,
301 308 # we're interested in it.
302 309 #
303 310 # - otherwise, we'll compare it with the single parent we found.
304 311 # If any of the files we're interested in is different in the
305 312 # the two revisions, we're interested in rev.
306 313
307 314 # A parent p is interesting if its mapped version (self.parentmap[p]):
308 315 # - is not SKIPREV
309 316 # - is still not in the list of parents (we don't want duplicates)
310 317 # - is not an ancestor of the mapped versions of the other parents or
311 318 # there is no parent in the same branch than the current revision.
312 319 mparents = []
313 320 knownparents = set()
314 321 branch = self.commits[rev].branch
315 322 hasbranchparent = False
316 323 for i, p1 in enumerate(parents):
317 324 mp1 = self.parentmap[p1]
318 325 if mp1 == SKIPREV or mp1 in knownparents:
319 326 continue
320 327 isancestor = util.any(p2 for p2 in parents
321 328 if p1 != p2 and mp1 != self.parentmap[p2]
322 329 and mp1 in self.wantedancestors[p2])
323 330 if not isancestor and not hasbranchparent and len(parents) > 1:
324 331 # This could be expensive, avoid unnecessary calls.
325 332 if self._cachedcommit(p1).branch == branch:
326 333 hasbranchparent = True
327 334 mparents.append((p1, mp1, i, isancestor))
328 335 knownparents.add(mp1)
329 336 # Discard parents ancestors of other parents if there is a
330 337 # non-ancestor one on the same branch than current revision.
331 338 if hasbranchparent:
332 339 mparents = [p for p in mparents if not p[3]]
333 340 wp = None
334 341 if mparents:
335 342 wp = max(p[2] for p in mparents)
336 343 mparents = [p[1] for p in mparents]
337 344 elif parents:
338 345 wp = 0
339 346
340 347 self.origparents[rev] = parents
341 348
342 349 closed = False
343 350 if 'close' in self.commits[rev].extra:
344 351 # A branch closing revision is only useful if one of its
345 352 # parents belong to the branch being closed
346 353 pbranches = [self._cachedcommit(p).branch for p in mparents]
347 354 if branch in pbranches:
348 355 closed = True
349 356
350 357 if len(mparents) < 2 and not closed and not self.wanted(rev, wp):
351 358 # We don't want this revision.
352 359 # Update our state and tell the convert process to map this
353 360 # revision to the same revision its parent as mapped to.
354 361 p = None
355 362 if parents:
356 363 p = parents[wp]
357 364 self.mark_not_wanted(rev, p)
358 365 self.convertedorder.append((rev, False, p))
359 366 self._discard(*parents)
360 367 return self.parentmap[rev]
361 368
362 369 # We want this revision.
363 370 # Rewrite the parents of the commit object
364 371 self.commits[rev].parents = mparents
365 372 self.mark_wanted(rev, parents)
366 373 self.convertedorder.append((rev, True, None))
367 374 self._discard(*parents)
368 375
369 376 # Get the real changes and do the filtering/mapping. To be
370 377 # able to get the files later on in getfile, we hide the
371 378 # original filename in the rev part of the return value.
372 379 changes, copies = self.base.getchanges(rev)
373 380 files = {}
374 381 for f, r in changes:
375 382 newf = self.filemapper(f)
376 383 if newf and (newf != f or newf not in files):
377 384 files[newf] = (f, r)
378 385 files = sorted(files.items())
379 386
380 387 ncopies = {}
381 388 for c in copies:
382 389 newc = self.filemapper(c)
383 390 if newc:
384 391 newsource = self.filemapper(copies[c])
385 392 if newsource:
386 393 ncopies[newc] = newsource
387 394
388 395 return files, ncopies
389 396
390 397 def getfile(self, name, rev):
391 398 realname, realrev = rev
392 399 return self.base.getfile(realname, realrev)
393 400
394 401 def gettags(self):
395 402 return self.base.gettags()
396 403
397 404 def hasnativeorder(self):
398 405 return self.base.hasnativeorder()
399 406
400 407 def lookuprev(self, rev):
401 408 return self.base.lookuprev(rev)
402 409
403 410 def getbookmarks(self):
404 411 return self.base.getbookmarks()
@@ -1,630 +1,656
1 1
2 2 $ HGMERGE=true; export HGMERGE
3 3 $ echo '[extensions]' >> $HGRCPATH
4 4 $ echo 'graphlog =' >> $HGRCPATH
5 5 $ echo 'convert =' >> $HGRCPATH
6 6 $ glog()
7 7 > {
8 8 > hg glog --template '{rev} "{desc}" files: {files}\n' "$@"
9 9 > }
10 10 $ hg init source
11 11 $ cd source
12 12 $ echo foo > foo
13 13 $ echo baz > baz
14 14 $ mkdir -p dir/subdir
15 15 $ echo dir/file >> dir/file
16 16 $ echo dir/file2 >> dir/file2
17 17 $ echo dir/file3 >> dir/file3 # to be corrupted in rev 0
18 18 $ echo dir/subdir/file3 >> dir/subdir/file3
19 19 $ echo dir/subdir/file4 >> dir/subdir/file4
20 20 $ hg ci -d '0 0' -qAm '0: add foo baz dir/'
21 21 $ echo bar > bar
22 22 $ echo quux > quux
23 23 $ echo dir/file4 >> dir/file4 # to be corrupted in rev 1
24 24 $ hg copy foo copied
25 25 $ hg ci -d '1 0' -qAm '1: add bar quux; copy foo to copied'
26 26 $ echo >> foo
27 27 $ hg ci -d '2 0' -m '2: change foo'
28 28 $ hg up -qC 1
29 29 $ echo >> bar
30 30 $ echo >> quux
31 31 $ hg ci -d '3 0' -m '3: change bar quux'
32 32 created new head
33 33 $ hg up -qC 2
34 34 $ hg merge -qr 3
35 35 $ echo >> bar
36 36 $ echo >> baz
37 37 $ hg ci -d '4 0' -m '4: first merge; change bar baz'
38 38 $ echo >> bar
39 39 $ echo 1 >> baz
40 40 $ echo >> quux
41 41 $ hg ci -d '5 0' -m '5: change bar baz quux'
42 42 $ hg up -qC 4
43 43 $ echo >> foo
44 44 $ echo 2 >> baz
45 45 $ hg ci -d '6 0' -m '6: change foo baz'
46 46 created new head
47 47 $ hg up -qC 5
48 48 $ hg merge -qr 6
49 49 $ echo >> bar
50 50 $ hg ci -d '7 0' -m '7: second merge; change bar'
51 51 $ echo >> foo
52 52 $ hg ci -m '8: change foo'
53 53 $ glog
54 54 @ 8 "8: change foo" files: foo
55 55 |
56 56 o 7 "7: second merge; change bar" files: bar baz
57 57 |\
58 58 | o 6 "6: change foo baz" files: baz foo
59 59 | |
60 60 o | 5 "5: change bar baz quux" files: bar baz quux
61 61 |/
62 62 o 4 "4: first merge; change bar baz" files: bar baz
63 63 |\
64 64 | o 3 "3: change bar quux" files: bar quux
65 65 | |
66 66 o | 2 "2: change foo" files: foo
67 67 |/
68 68 o 1 "1: add bar quux; copy foo to copied" files: bar copied dir/file4 quux
69 69 |
70 70 o 0 "0: add foo baz dir/" files: baz dir/file dir/file2 dir/file3 dir/subdir/file3 dir/subdir/file4 foo
71 71
72 72
73 73 final file versions in this repo:
74 74
75 75 $ hg manifest --debug
76 76 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
77 77 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
78 78 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
79 79 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir/file
80 80 75e6d3f8328f5f6ace6bf10b98df793416a09dca 644 dir/file2
81 81 e96dce0bc6a217656a3a410e5e6bec2c4f42bf7c 644 dir/file3
82 82 6edd55f559cdce67132b12ca09e09cee08b60442 644 dir/file4
83 83 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir/subdir/file3
84 84 57a1c1511590f3de52874adfa04effe8a77d64af 644 dir/subdir/file4
85 85 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
86 86 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
87 87 $ hg debugrename copied
88 88 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
89 89
90 90 $ cd ..
91 91
92 92
93 93 Test interaction with startrev and verify that changing it is handled properly:
94 94
95 95 $ > empty
96 96 $ hg convert --filemap empty source movingstart --config convert.hg.startrev=3 -r4
97 97 initializing destination movingstart repository
98 98 scanning source...
99 99 sorting...
100 100 converting...
101 101 1 3: change bar quux
102 102 0 4: first merge; change bar baz
103 103 $ hg convert --filemap empty source movingstart
104 104 scanning source...
105 105 sorting...
106 106 converting...
107 107 3 5: change bar baz quux
108 108 2 6: change foo baz
109 109 1 7: second merge; change bar
110 110 warning: af455ce4166b3c9c88e6309c2b9332171dcea595 parent 61e22ca76c3b3e93df20338c4e02ce286898e825 is missing
111 111 warning: cf908b3eeedc301c9272ebae931da966d5b326c7 parent 59e1ab45c888289513b7354484dac8a88217beab is missing
112 112 0 8: change foo
113 113
114 114
115 115 splitrepo tests
116 116
117 117 $ splitrepo()
118 118 > {
119 119 > msg="$1"
120 120 > files="$2"
121 121 > opts=$3
122 122 > echo "% $files: $msg"
123 123 > prefix=`echo "$files" | sed -e 's/ /-/g'`
124 124 > fmap="$prefix.fmap"
125 125 > repo="$prefix.repo"
126 126 > for i in $files; do
127 127 > echo "include $i" >> "$fmap"
128 128 > done
129 129 > hg -q convert $opts --filemap "$fmap" --datesort source "$repo"
130 130 > hg up -q -R "$repo"
131 131 > glog -R "$repo"
132 132 > hg -R "$repo" manifest --debug
133 133 > }
134 134 $ splitrepo 'skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd' foo
135 135 % foo: skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd
136 136 @ 3 "8: change foo" files: foo
137 137 |
138 138 o 2 "6: change foo baz" files: foo
139 139 |
140 140 o 1 "2: change foo" files: foo
141 141 |
142 142 o 0 "0: add foo baz dir/" files: foo
143 143
144 144 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
145 145 $ splitrepo 'merges are not merges anymore' bar
146 146 % bar: merges are not merges anymore
147 147 @ 4 "7: second merge; change bar" files: bar
148 148 |
149 149 o 3 "5: change bar baz quux" files: bar
150 150 |
151 151 o 2 "4: first merge; change bar baz" files: bar
152 152 |
153 153 o 1 "3: change bar quux" files: bar
154 154 |
155 155 o 0 "1: add bar quux; copy foo to copied" files: bar
156 156
157 157 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
158 158 $ splitrepo '1st merge is not a merge anymore; 2nd still is' baz
159 159 % baz: 1st merge is not a merge anymore; 2nd still is
160 160 @ 4 "7: second merge; change bar" files: baz
161 161 |\
162 162 | o 3 "6: change foo baz" files: baz
163 163 | |
164 164 o | 2 "5: change bar baz quux" files: baz
165 165 |/
166 166 o 1 "4: first merge; change bar baz" files: baz
167 167 |
168 168 o 0 "0: add foo baz dir/" files: baz
169 169
170 170 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
171 171 $ splitrepo 'we add additional merges when they are interesting' 'foo quux'
172 172 % foo quux: we add additional merges when they are interesting
173 173 @ 8 "8: change foo" files: foo
174 174 |
175 175 o 7 "7: second merge; change bar" files:
176 176 |\
177 177 | o 6 "6: change foo baz" files: foo
178 178 | |
179 179 o | 5 "5: change bar baz quux" files: quux
180 180 |/
181 181 o 4 "4: first merge; change bar baz" files:
182 182 |\
183 183 | o 3 "3: change bar quux" files: quux
184 184 | |
185 185 o | 2 "2: change foo" files: foo
186 186 |/
187 187 o 1 "1: add bar quux; copy foo to copied" files: quux
188 188 |
189 189 o 0 "0: add foo baz dir/" files: foo
190 190
191 191 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
192 192 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
193 193 $ splitrepo 'partial conversion' 'bar quux' '-r 3'
194 194 % bar quux: partial conversion
195 195 @ 1 "3: change bar quux" files: bar quux
196 196 |
197 197 o 0 "1: add bar quux; copy foo to copied" files: bar quux
198 198
199 199 b79105bedc55102f394e90a789c9c380117c1b4a 644 bar
200 200 db0421cc6b685a458c8d86c7d5c004f94429ea23 644 quux
201 201 $ splitrepo 'complete the partial conversion' 'bar quux'
202 202 % bar quux: complete the partial conversion
203 203 @ 4 "7: second merge; change bar" files: bar
204 204 |
205 205 o 3 "5: change bar baz quux" files: bar quux
206 206 |
207 207 o 2 "4: first merge; change bar baz" files: bar
208 208 |
209 209 o 1 "3: change bar quux" files: bar quux
210 210 |
211 211 o 0 "1: add bar quux; copy foo to copied" files: bar quux
212 212
213 213 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
214 214 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
215 215 $ rm -r foo.repo
216 216 $ splitrepo 'partial conversion' 'foo' '-r 3'
217 217 % foo: partial conversion
218 218 @ 0 "0: add foo baz dir/" files: foo
219 219
220 220 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
221 221 $ splitrepo 'complete the partial conversion' 'foo'
222 222 % foo: complete the partial conversion
223 223 @ 3 "8: change foo" files: foo
224 224 |
225 225 o 2 "6: change foo baz" files: foo
226 226 |
227 227 o 1 "2: change foo" files: foo
228 228 |
229 229 o 0 "0: add foo baz dir/" files: foo
230 230
231 231 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
232 232 $ splitrepo 'copied file; source not included in new repo' copied
233 233 % copied: copied file; source not included in new repo
234 234 @ 0 "1: add bar quux; copy foo to copied" files: copied
235 235
236 236 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 copied
237 237 $ hg --cwd copied.repo debugrename copied
238 238 copied not renamed
239 239 $ splitrepo 'copied file; source included in new repo' 'foo copied'
240 240 % foo copied: copied file; source included in new repo
241 241 @ 4 "8: change foo" files: foo
242 242 |
243 243 o 3 "6: change foo baz" files: foo
244 244 |
245 245 o 2 "2: change foo" files: foo
246 246 |
247 247 o 1 "1: add bar quux; copy foo to copied" files: copied
248 248 |
249 249 o 0 "0: add foo baz dir/" files: foo
250 250
251 251 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
252 252 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
253 253 $ hg --cwd foo-copied.repo debugrename copied
254 254 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
255 255
256 256 ensure that the filemap contains duplicated slashes (issue3612)
257 257
258 258 $ cat > renames.fmap <<EOF
259 259 > include dir
260 260 > exclude dir/file2
261 261 > rename dir dir2//dir3
262 262 > include foo
263 263 > include copied
264 264 > rename foo foo2/
265 265 > rename copied ./copied2
266 266 > exclude dir/subdir
267 267 > include dir/subdir/file3
268 268 > EOF
269 269 $ rm source/.hg/store/data/dir/file3.i
270 270 $ rm source/.hg/store/data/dir/file4.i
271 271 $ hg -q convert --filemap renames.fmap --datesort source dummydest
272 272 abort: data/dir/file3.i@e96dce0bc6a2: no match found!
273 273 [255]
274 274 $ hg -q convert --filemap renames.fmap --datesort --config convert.hg.ignoreerrors=1 source renames.repo
275 275 ignoring: data/dir/file3.i@e96dce0bc6a2: no match found
276 276 ignoring: data/dir/file4.i@6edd55f559cd: no match found
277 277 $ hg up -q -R renames.repo
278 278 $ glog -R renames.repo
279 279 @ 4 "8: change foo" files: foo2
280 280 |
281 281 o 3 "6: change foo baz" files: foo2
282 282 |
283 283 o 2 "2: change foo" files: foo2
284 284 |
285 285 o 1 "1: add bar quux; copy foo to copied" files: copied2
286 286 |
287 287 o 0 "0: add foo baz dir/" files: dir2/dir3/file dir2/dir3/subdir/file3 foo2
288 288
289 289 $ hg -R renames.repo verify
290 290 checking changesets
291 291 checking manifests
292 292 crosschecking files in changesets and manifests
293 293 checking files
294 294 4 files, 5 changesets, 7 total revisions
295 295
296 296 $ hg -R renames.repo manifest --debug
297 297 d43feacba7a4f1f2080dde4a4b985bd8a0236d46 644 copied2
298 298 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir2/dir3/file
299 299 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir2/dir3/subdir/file3
300 300 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo2
301 301 $ hg --cwd renames.repo debugrename copied2
302 302 copied2 renamed from foo2:2ed2a3912a0b24502043eae84ee4b279c18b90dd
303 303
304 304 copied:
305 305
306 306 $ hg --cwd source cat copied
307 307 foo
308 308
309 309 copied2:
310 310
311 311 $ hg --cwd renames.repo cat copied2
312 312 foo
313 313
314 314 filemap errors
315 315
316 316 $ cat > errors.fmap <<EOF
317 317 > include dir/ # beware that comments changes error line numbers!
318 318 > exclude /dir
319 319 > rename dir//dir /dir//dir/ "out of sync"
320 320 > include
321 321 > EOF
322 322 $ hg -q convert --filemap errors.fmap source errors.repo
323 323 errors.fmap:3: superfluous / in include '/dir'
324 324 errors.fmap:3: superfluous / in rename '/dir'
325 325 errors.fmap:4: unknown directive 'out of sync'
326 326 errors.fmap:5: path to exclude is missing
327 327 abort: errors in filemap
328 328 [255]
329 329
330 330 test branch closing revision pruning if branch is pruned
331 331
332 332 $ hg init branchpruning
333 333 $ cd branchpruning
334 334 $ hg branch foo
335 335 marked working directory as branch foo
336 336 (branches are permanent and global, did you want a bookmark?)
337 337 $ echo a > a
338 338 $ hg ci -Am adda
339 339 adding a
340 340 $ hg ci --close-branch -m closefoo
341 341 $ hg up 0
342 342 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 343 $ hg branch empty
344 344 marked working directory as branch empty
345 345 (branches are permanent and global, did you want a bookmark?)
346 346 $ hg ci -m emptybranch
347 347 $ hg ci --close-branch -m closeempty
348 348 $ hg up 0
349 349 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 350 $ hg branch default
351 351 marked working directory as branch default
352 352 (branches are permanent and global, did you want a bookmark?)
353 353 $ echo b > b
354 354 $ hg ci -Am addb
355 355 adding b
356 356 $ hg ci --close-branch -m closedefault
357 357 $ cat > filemap <<EOF
358 358 > include b
359 359 > EOF
360 360 $ cd ..
361 361 $ hg convert branchpruning branchpruning-hg1
362 362 initializing destination branchpruning-hg1 repository
363 363 scanning source...
364 364 sorting...
365 365 converting...
366 366 5 adda
367 367 4 closefoo
368 368 3 emptybranch
369 369 2 closeempty
370 370 1 addb
371 371 0 closedefault
372 372 $ glog -R branchpruning-hg1
373 373 o 5 "closedefault" files:
374 374 |
375 375 o 4 "addb" files: b
376 376 |
377 377 | o 3 "closeempty" files:
378 378 | |
379 379 | o 2 "emptybranch" files:
380 380 |/
381 381 | o 1 "closefoo" files:
382 382 |/
383 383 o 0 "adda" files: a
384 384
385 385
386 386 exercise incremental conversion at the same time
387 387
388 388 $ hg convert -r0 --filemap branchpruning/filemap branchpruning branchpruning-hg2
389 389 initializing destination branchpruning-hg2 repository
390 390 scanning source...
391 391 sorting...
392 392 converting...
393 393 0 adda
394 394 $ hg convert -r4 --filemap branchpruning/filemap branchpruning branchpruning-hg2
395 395 scanning source...
396 396 sorting...
397 397 converting...
398 398 0 addb
399 399 $ hg convert --filemap branchpruning/filemap branchpruning branchpruning-hg2
400 400 scanning source...
401 401 sorting...
402 402 converting...
403 403 3 closefoo
404 404 2 emptybranch
405 405 1 closeempty
406 406 0 closedefault
407 407 $ glog -R branchpruning-hg2
408 408 o 1 "closedefault" files:
409 409 |
410 410 o 0 "addb" files: b
411 411
412 412
413 Test rebuilding of map with unknown revisions in shamap - it used to crash
414
415 $ cd branchpruning
416 $ hg up -r 2
417 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
418 $ hg merge 4
419 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
420 (branch merge, don't forget to commit)
421 $ hg ci -m 'merging something'
422 $ cd ..
423 $ echo "53792d18237d2b64971fa571936869156655338d 6d955580116e82c4b029bd30f321323bae71a7f0" >> branchpruning-hg2/.hg/shamap
424 $ hg convert --filemap branchpruning/filemap branchpruning branchpruning-hg2 --debug
425 run hg source pre-conversion action
426 run hg sink pre-conversion action
427 scanning source...
428 scanning: 1 revisions
429 sorting...
430 converting...
431 0 merging something
432 source: 2503605b178fe50e8fbbb0e77b97939540aa8c87
433 converting: 0/1 revisions (0.00%)
434 unknown revmap source: 53792d18237d2b64971fa571936869156655338d
435 run hg sink post-conversion action
436 run hg source post-conversion action
437
438
413 439 filemap rename undoing revision rename
414 440
415 441 $ hg init renameundo
416 442 $ cd renameundo
417 443 $ echo 1 > a
418 444 $ echo 1 > c
419 445 $ hg ci -qAm add
420 446 $ hg mv -q a b/a
421 447 $ hg mv -q c b/c
422 448 $ hg ci -qm rename
423 449 $ echo 2 > b/a
424 450 $ echo 2 > b/c
425 451 $ hg ci -qm modify
426 452 $ cd ..
427 453
428 454 $ echo "rename b ." > renameundo.fmap
429 455 $ hg convert --filemap renameundo.fmap renameundo renameundo2
430 456 initializing destination renameundo2 repository
431 457 scanning source...
432 458 sorting...
433 459 converting...
434 460 2 add
435 461 1 rename
436 462 filtering out empty revision
437 463 repository tip rolled back to revision 0 (undo commit)
438 464 0 modify
439 465 $ glog -R renameundo2
440 466 o 1 "modify" files: a c
441 467 |
442 468 o 0 "add" files: a c
443 469
444 470
445 471
446 472 test merge parents/empty merges pruning
447 473
448 474 $ glog()
449 475 > {
450 476 > hg glog --template '{rev}:{node|short}@{branch} "{desc}" files: {files}\n' "$@"
451 477 > }
452 478
453 479 test anonymous branch pruning
454 480
455 481 $ hg init anonymousbranch
456 482 $ cd anonymousbranch
457 483 $ echo a > a
458 484 $ echo b > b
459 485 $ hg ci -Am add
460 486 adding a
461 487 adding b
462 488 $ echo a >> a
463 489 $ hg ci -m changea
464 490 $ hg up 0
465 491 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
466 492 $ echo b >> b
467 493 $ hg ci -m changeb
468 494 created new head
469 495 $ hg up 1
470 496 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 497 $ hg merge
472 498 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
473 499 (branch merge, don't forget to commit)
474 500 $ hg ci -m merge
475 501 $ cd ..
476 502
477 503 $ cat > filemap <<EOF
478 504 > include a
479 505 > EOF
480 506 $ hg convert --filemap filemap anonymousbranch anonymousbranch-hg
481 507 initializing destination anonymousbranch-hg repository
482 508 scanning source...
483 509 sorting...
484 510 converting...
485 511 3 add
486 512 2 changea
487 513 1 changeb
488 514 0 merge
489 515 $ glog -R anonymousbranch
490 516 @ 3:c71d5201a498@default "merge" files:
491 517 |\
492 518 | o 2:607eb44b17f9@default "changeb" files: b
493 519 | |
494 520 o | 1:1f60ea617824@default "changea" files: a
495 521 |/
496 522 o 0:0146e6129113@default "add" files: a b
497 523
498 524 $ glog -R anonymousbranch-hg
499 525 o 1:cda818e7219b@default "changea" files: a
500 526 |
501 527 o 0:c334dc3be0da@default "add" files: a
502 528
503 529 $ cat anonymousbranch-hg/.hg/shamap
504 530 0146e6129113dba9ac90207cfdf2d7ed35257ae5 c334dc3be0daa2a4e9ce4d2e2bdcba40c09d4916
505 531 1f60ea61782421edf8d051ff4fcb61b330f26a4a cda818e7219b5f7f3fb9f49780054ed6a1905ec3
506 532 607eb44b17f9348cd5cbd26e16af87ba77b0b037 c334dc3be0daa2a4e9ce4d2e2bdcba40c09d4916
507 533 c71d5201a498b2658d105a6bf69d7a0df2649aea cda818e7219b5f7f3fb9f49780054ed6a1905ec3
508 534
509 535 $ cat > filemap <<EOF
510 536 > include b
511 537 > EOF
512 538 $ hg convert --filemap filemap anonymousbranch anonymousbranch-hg2
513 539 initializing destination anonymousbranch-hg2 repository
514 540 scanning source...
515 541 sorting...
516 542 converting...
517 543 3 add
518 544 2 changea
519 545 1 changeb
520 546 0 merge
521 547 $ glog -R anonymousbranch
522 548 @ 3:c71d5201a498@default "merge" files:
523 549 |\
524 550 | o 2:607eb44b17f9@default "changeb" files: b
525 551 | |
526 552 o | 1:1f60ea617824@default "changea" files: a
527 553 |/
528 554 o 0:0146e6129113@default "add" files: a b
529 555
530 556 $ glog -R anonymousbranch-hg2
531 557 o 1:62dd350b0df6@default "changeb" files: b
532 558 |
533 559 o 0:4b9ced861657@default "add" files: b
534 560
535 561 $ cat anonymousbranch-hg2/.hg/shamap
536 562 0146e6129113dba9ac90207cfdf2d7ed35257ae5 4b9ced86165703791653059a1db6ed864630a523
537 563 1f60ea61782421edf8d051ff4fcb61b330f26a4a 4b9ced86165703791653059a1db6ed864630a523
538 564 607eb44b17f9348cd5cbd26e16af87ba77b0b037 62dd350b0df695f7d2c82a02e0499b16fd790f22
539 565 c71d5201a498b2658d105a6bf69d7a0df2649aea 62dd350b0df695f7d2c82a02e0499b16fd790f22
540 566
541 567 test named branch pruning
542 568
543 569 $ hg init namedbranch
544 570 $ cd namedbranch
545 571 $ echo a > a
546 572 $ echo b > b
547 573 $ hg ci -Am add
548 574 adding a
549 575 adding b
550 576 $ echo a >> a
551 577 $ hg ci -m changea
552 578 $ hg up 0
553 579 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 580 $ hg branch foo
555 581 marked working directory as branch foo
556 582 (branches are permanent and global, did you want a bookmark?)
557 583 $ echo b >> b
558 584 $ hg ci -m changeb
559 585 $ hg up default
560 586 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 587 $ hg merge foo
562 588 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
563 589 (branch merge, don't forget to commit)
564 590 $ hg ci -m merge
565 591 $ cd ..
566 592
567 593 $ cat > filemap <<EOF
568 594 > include a
569 595 > EOF
570 596 $ hg convert --filemap filemap namedbranch namedbranch-hg
571 597 initializing destination namedbranch-hg repository
572 598 scanning source...
573 599 sorting...
574 600 converting...
575 601 3 add
576 602 2 changea
577 603 1 changeb
578 604 0 merge
579 605 $ glog -R namedbranch
580 606 @ 3:73899bcbe45c@default "merge" files:
581 607 |\
582 608 | o 2:8097982d19fc@foo "changeb" files: b
583 609 | |
584 610 o | 1:1f60ea617824@default "changea" files: a
585 611 |/
586 612 o 0:0146e6129113@default "add" files: a b
587 613
588 614 $ glog -R namedbranch-hg
589 615 o 1:cda818e7219b@default "changea" files: a
590 616 |
591 617 o 0:c334dc3be0da@default "add" files: a
592 618
593 619
594 620 $ cd namedbranch
595 621 $ hg --config extensions.mq= strip tip
596 622 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
597 623 saved backup bundle to $TESTTMP/namedbranch/.hg/strip-backup/73899bcbe45c-backup.hg (glob)
598 624 $ hg up foo
599 625 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
600 626 $ hg merge default
601 627 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
602 628 (branch merge, don't forget to commit)
603 629 $ hg ci -m merge
604 630 $ cd ..
605 631
606 632 $ hg convert --filemap filemap namedbranch namedbranch-hg2
607 633 initializing destination namedbranch-hg2 repository
608 634 scanning source...
609 635 sorting...
610 636 converting...
611 637 3 add
612 638 2 changea
613 639 1 changeb
614 640 0 merge
615 641 $ glog -R namedbranch
616 642 @ 3:e1959de76e1b@foo "merge" files:
617 643 |\
618 644 | o 2:8097982d19fc@foo "changeb" files: b
619 645 | |
620 646 o | 1:1f60ea617824@default "changea" files: a
621 647 |/
622 648 o 0:0146e6129113@default "add" files: a b
623 649
624 650 $ glog -R namedbranch-hg2
625 651 o 2:dcf314454667@foo "merge" files:
626 652 |\
627 653 | o 1:cda818e7219b@default "changea" files: a
628 654 |/
629 655 o 0:c334dc3be0da@default "add" files: a
630 656
General Comments 0
You need to be logged in to leave comments. Login now