##// END OF EJS Templates
rebase: sort destmap topologically...
Jun Wu -
r34008:32528419 default
parent child Browse files
Show More
@@ -361,7 +361,7 b' class rebaseruntime(object):'
361 self.ui.status(_('reopening closed branch head %s\n') % dest)
361 self.ui.status(_('reopening closed branch head %s\n') % dest)
362
362
363 def _performrebase(self, tr):
363 def _performrebase(self, tr):
364 repo, ui, opts = self.repo, self.ui, self.opts
364 repo, ui = self.repo, self.ui
365 if self.keepbranchesf:
365 if self.keepbranchesf:
366 # insert _savebranch at the start of extrafns so if
366 # insert _savebranch at the start of extrafns so if
367 # there's a user-provided extrafn it can clobber branch if
367 # there's a user-provided extrafn it can clobber branch if
@@ -384,10 +384,17 b' class rebaseruntime(object):'
384 # if we fail before the transaction closes.
384 # if we fail before the transaction closes.
385 self.storestatus()
385 self.storestatus()
386
386
387 sortedrevs = repo.revs('sort(%ld, -topo)', self.state)
388 cands = [k for k, v in self.state.iteritems() if v == revtodo]
387 cands = [k for k, v in self.state.iteritems() if v == revtodo]
389 total = len(cands)
388 total = len(cands)
390 pos = 0
389 pos = 0
390 for subset in sortsource(self.destmap):
391 pos = self._performrebasesubset(tr, subset, pos, total)
392 ui.progress(_('rebasing'), None)
393 ui.note(_('rebase merging completed\n'))
394
395 def _performrebasesubset(self, tr, subset, pos, total):
396 repo, ui, opts = self.repo, self.ui, self.opts
397 sortedrevs = repo.revs('sort(%ld, -topo)', subset)
391 for rev in sortedrevs:
398 for rev in sortedrevs:
392 dest = self.destmap[rev]
399 dest = self.destmap[rev]
393 ctx = repo[rev]
400 ctx = repo[rev]
@@ -449,9 +456,7 b' class rebaseruntime(object):'
449 else:
456 else:
450 ui.status(_('already rebased %s as %s\n') %
457 ui.status(_('already rebased %s as %s\n') %
451 (desc, repo[self.state[rev]]))
458 (desc, repo[self.state[rev]]))
452
459 return pos
453 ui.progress(_('rebasing'), None)
454 ui.note(_('rebase merging completed\n'))
455
460
456 def _finishrebase(self):
461 def _finishrebase(self):
457 repo, ui, opts = self.repo, self.ui, self.opts
462 repo, ui, opts = self.repo, self.ui, self.opts
@@ -969,6 +974,18 b' def adjustdest(repo, rev, destmap, state'
969 | B | ...
974 | B | ...
970 |/ |/
975 |/ |/
971 A A
976 A A
977
978 Besides, adjust dest according to existing rebase information. For example,
979
980 B C D B needs to be rebased on top of C, C needs to be rebased on top
981 \|/ of D. We will rebase C first.
982 A
983
984 C' After rebasing C, when considering B's destination, use C'
985 | instead of the original C.
986 B D
987 \ /
988 A
972 """
989 """
973 # pick already rebased revs with same dest from state as interesting source
990 # pick already rebased revs with same dest from state as interesting source
974 dest = destmap[rev]
991 dest = destmap[rev]
@@ -981,6 +998,12 b' def adjustdest(repo, rev, destmap, state'
981 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
998 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
982 if candidate is not None:
999 if candidate is not None:
983 adjusted = state[candidate]
1000 adjusted = state[candidate]
1001 if adjusted == dest and dest in state:
1002 adjusted = state[dest]
1003 if adjusted == revtodo:
1004 # sortsource should produce an order that makes this impossible
1005 raise error.ProgrammingError(
1006 'rev %d should be rebased already at this time' % dest)
984 result.append(adjusted)
1007 result.append(adjusted)
985 return result
1008 return result
986
1009
@@ -1128,6 +1151,12 b' def defineparents(repo, rev, destmap, st'
1128 'moving at least one of its parents')
1151 'moving at least one of its parents')
1129 % (rev, repo[rev]))
1152 % (rev, repo[rev]))
1130
1153
1154 # Source should not be ancestor of dest. The check here guarantees it's
1155 # impossible. With multi-dest, the initial check does not cover complex
1156 # cases since we don't have abstractions to dry-run rebase cheaply.
1157 if any(p != nullrev and isancestor(rev, p) for p in newps):
1158 raise error.Abort(_('source is ancestor of destination'))
1159
1131 # "rebasenode" updates to new p1, use the corresponding merge base.
1160 # "rebasenode" updates to new p1, use the corresponding merge base.
1132 if bases[0] != nullrev:
1161 if bases[0] != nullrev:
1133 base = bases[0]
1162 base = bases[0]
@@ -1354,6 +1383,31 b' def abort(repo, originalwd, destmap, sta'
1354 repo.ui.warn(_('rebase aborted\n'))
1383 repo.ui.warn(_('rebase aborted\n'))
1355 return 0
1384 return 0
1356
1385
1386 def sortsource(destmap):
1387 """yield source revisions in an order that we only rebase things once
1388
1389 If source and destination overlaps, we should filter out revisions
1390 depending on other revisions which hasn't been rebased yet.
1391
1392 Yield a sorted list of revisions each time.
1393
1394 For example, when rebasing A to B, B to C. This function yields [B], then
1395 [A], indicating B needs to be rebased first.
1396
1397 Raise if there is a cycle so the rebase is impossible.
1398 """
1399 srcset = set(destmap)
1400 while srcset:
1401 srclist = sorted(srcset)
1402 result = []
1403 for r in srclist:
1404 if destmap[r] not in srcset:
1405 result.append(r)
1406 if not result:
1407 raise error.Abort(_('source and destination form a cycle'))
1408 srcset -= set(result)
1409 yield result
1410
1357 def buildstate(repo, destmap, collapse, obsoletenotrebased):
1411 def buildstate(repo, destmap, collapse, obsoletenotrebased):
1358 '''Define which revisions are going to be rebased and where
1412 '''Define which revisions are going to be rebased and where
1359
1413
@@ -1372,12 +1426,21 b' def buildstate(repo, destmap, collapse, '
1372 if set(destmap.values()) & mqapplied:
1426 if set(destmap.values()) & mqapplied:
1373 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1427 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1374
1428
1375 roots = list(repo.set('roots(%ld)', rebaseset))
1429 # Get "cycle" error early by exhausting the generator.
1430 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1431 if not sortedsrc:
1432 raise error.Abort(_('no matching revisions'))
1433
1434 # Only check the first batch of revisions to rebase not depending on other
1435 # rebaseset. This means "source is ancestor of destination" for the second
1436 # (and following) batches of revisions are not checked here. We rely on
1437 # "defineparents" to do that check.
1438 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1376 if not roots:
1439 if not roots:
1377 raise error.Abort(_('no matching revisions'))
1440 raise error.Abort(_('no matching revisions'))
1378 roots.sort()
1441 roots.sort()
1379 state = dict.fromkeys(rebaseset, revtodo)
1442 state = dict.fromkeys(rebaseset, revtodo)
1380 emptyrebase = True
1443 emptyrebase = (len(sortedsrc) == 1)
1381 for root in roots:
1444 for root in roots:
1382 dest = repo[destmap[root.rev()]]
1445 dest = repo[destmap[root.rev()]]
1383 commonbase = root.ancestor(dest)
1446 commonbase = root.ancestor(dest)
@@ -204,7 +204,7 b' Destination is an ancestor of source:'
204 > |
204 > |
205 > Z
205 > Z
206 > EOS
206 > EOS
207 abort: source is ancestor of destination
207 abort: source and destination form a cycle
208 [255]
208 [255]
209
209
210 Switch roots:
210 Switch roots:
@@ -291,22 +291,163 b' Move to a previous parent:'
291 |/
291 |/
292 o 0: A
292 o 0: A
293
293
294 Source overlaps with destination (not handled well currently):
294 Source overlaps with destination:
295
295
296 $ rebasewithdag -s 'B+C+D' -d 'map(SRC, "B:C,C:D")' <<'EOS'
296 $ rebasewithdag -s 'B+C+D' -d 'map(SRC, "B:C,C:D")' <<'EOS'
297 > B C D
297 > B C D
298 > \|/
298 > \|/
299 > A
299 > A
300 > EOS
300 > EOS
301 rebasing 2:dc0947a82db8 "C" (C)
301 rebasing 1:112478962961 "B" (B)
302 rebasing 1:112478962961 "B" (B)
302 rebasing 2:dc0947a82db8 "C" (C)
303 o 5: B
303 o 5: C
304 |
305 o 4: C
306 |
307 o 3: D
308 |
309 o 0: A
310
311 Detect cycles early:
312
313 $ rebasewithdag -r 'all()-Z' -d 'map(SRC, "A:B,B:C,C:D,D:B")' <<'EOS'
314 > A B C
315 > \|/
316 > | D
317 > |/
318 > Z
319 > EOS
320 abort: source and destination form a cycle
321 [255]
322
323 Detect source is ancestor of dest in runtime:
324
325 $ rebasewithdag -r 'C+B' -d 'map(SRC, "C:B,B:D")' -q <<'EOS'
326 > D
327 > |
328 > B C
329 > \|
330 > A
331 > EOS
332 abort: source is ancestor of destination
333 [255]
334
335 "Already rebased" fast path still works:
336
337 $ rebasewithdag -r 'all()' -d 'SRC^' <<'EOS'
338 > E F
339 > /| |
340 > B C D
341 > \|/
342 > A
343 > EOS
344 already rebased 1:112478962961 "B" (B)
345 already rebased 2:dc0947a82db8 "C" (C)
346 already rebased 3:b18e25de2cf5 "D" (D)
347 already rebased 4:312782b8f06e "E" (E)
348 already rebased 5:ad6717a6a58e "F" (F tip)
349 o 5: F
304 |
350 |
305 o 3: D
351 o 3: D
306 |
352 |
307 | o 4: B orphan
353 | o 4: E
354 | |\
355 +---o 2: C
308 | |
356 | |
309 | x 2: C
357 | o 1: B
310 |/
358 |/
311 o 0: A
359 o 0: A
312
360
361 Massively rewrite the DAG:
362
363 $ rebasewithdag -r 'all()' -d 'map(SRC, "A:I,I:null,H:A,B:J,J:C,C:H,D:E,F:G,G:K,K:D,E:B")' <<'EOS'
364 > D G K
365 > | | |
366 > C F J
367 > | | |
368 > B E I
369 > \| |
370 > A H
371 > EOS
372 rebasing 4:701514e1408d "I" (I)
373 rebasing 0:426bada5c675 "A" (A)
374 rebasing 1:e7050b6e5048 "H" (H)
375 rebasing 5:26805aba1e60 "C" (C)
376 rebasing 7:cf89f86b485b "J" (J)
377 rebasing 2:112478962961 "B" (B)
378 rebasing 3:7fb047a69f22 "E" (E)
379 rebasing 8:f585351a92f8 "D" (D)
380 rebasing 10:ae41898d7875 "K" (K tip)
381 rebasing 9:711f53bbef0b "G" (G)
382 rebasing 6:64a8289d2492 "F" (F)
383 o 21: F
384 |
385 o 20: G
386 |
387 o 19: K
388 |
389 o 18: D
390 |
391 o 17: E
392 |
393 o 16: B
394 |
395 o 15: J
396 |
397 o 14: C
398 |
399 o 13: H
400 |
401 o 12: A
402 |
403 o 11: I
404
405 Resolve instability:
406
407 $ rebasewithdag <<'EOF' -r 'orphan()-obsolete()' -d 'max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::)'
408 > F2
409 > |
410 > J E E2
411 > | |/
412 > I2 I | E3
413 > \| |/
414 > H | G
415 > | | |
416 > B2 D F
417 > | |/ # rebase: B -> B2
418 > N C # amend: E -> E2
419 > | | # amend: E2 -> E3
420 > M B # rebase: F -> F2
421 > \| # amend: I -> I2
422 > A
423 > EOF
424 rebasing 16:5c432343bf59 "J" (J tip)
425 rebasing 3:26805aba1e60 "C" (C)
426 rebasing 6:f585351a92f8 "D" (D)
427 rebasing 10:ffebc37c5d0b "E3" (E3)
428 rebasing 13:fb184bcfeee8 "F2" (F2)
429 rebasing 11:dc838ab4c0da "G" (G)
430 o 22: G
431 |
432 o 21: F2
433 |
434 o 20: E3
435 |
436 o 19: D
437 |
438 o 18: C
439 |
440 o 17: J
441 |
442 o 15: I2
443 |
444 o 12: H
445 |
446 o 5: B2
447 |
448 o 4: N
449 |
450 o 2: M
451 |
452 o 0: A
453
@@ -245,7 +245,7 b' Rebasing descendant onto ancestor across'
245 @ 0: 'A'
245 @ 0: 'A'
246
246
247 $ hg rebase -s 5 -d 6
247 $ hg rebase -s 5 -d 6
248 abort: source is ancestor of destination
248 abort: source and destination form a cycle
249 [255]
249 [255]
250
250
251 $ hg rebase -s 6 -d 5
251 $ hg rebase -s 6 -d 5
@@ -264,7 +264,7 b' G onto F - rebase onto an ancestor:'
264 F onto G - rebase onto a descendant:
264 F onto G - rebase onto a descendant:
265
265
266 $ hg rebase -s 5 -d 6
266 $ hg rebase -s 5 -d 6
267 abort: source is ancestor of destination
267 abort: source and destination form a cycle
268 [255]
268 [255]
269
269
270 G onto B - merge revision with both parents not in ancestors of target:
270 G onto B - merge revision with both parents not in ancestors of target:
General Comments 0
You need to be logged in to leave comments. Login now