##// 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 361 self.ui.status(_('reopening closed branch head %s\n') % dest)
362 362
363 363 def _performrebase(self, tr):
364 repo, ui, opts = self.repo, self.ui, self.opts
364 repo, ui = self.repo, self.ui
365 365 if self.keepbranchesf:
366 366 # insert _savebranch at the start of extrafns so if
367 367 # there's a user-provided extrafn it can clobber branch if
@@ -384,10 +384,17 b' class rebaseruntime(object):'
384 384 # if we fail before the transaction closes.
385 385 self.storestatus()
386 386
387 sortedrevs = repo.revs('sort(%ld, -topo)', self.state)
388 387 cands = [k for k, v in self.state.iteritems() if v == revtodo]
389 388 total = len(cands)
390 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 398 for rev in sortedrevs:
392 399 dest = self.destmap[rev]
393 400 ctx = repo[rev]
@@ -449,9 +456,7 b' class rebaseruntime(object):'
449 456 else:
450 457 ui.status(_('already rebased %s as %s\n') %
451 458 (desc, repo[self.state[rev]]))
452
453 ui.progress(_('rebasing'), None)
454 ui.note(_('rebase merging completed\n'))
459 return pos
455 460
456 461 def _finishrebase(self):
457 462 repo, ui, opts = self.repo, self.ui, self.opts
@@ -969,6 +974,18 b' def adjustdest(repo, rev, destmap, state'
969 974 | B | ...
970 975 |/ |/
971 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 990 # pick already rebased revs with same dest from state as interesting source
974 991 dest = destmap[rev]
@@ -981,6 +998,12 b' def adjustdest(repo, rev, destmap, state'
981 998 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
982 999 if candidate is not None:
983 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 1007 result.append(adjusted)
985 1008 return result
986 1009
@@ -1128,6 +1151,12 b' def defineparents(repo, rev, destmap, st'
1128 1151 'moving at least one of its parents')
1129 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 1160 # "rebasenode" updates to new p1, use the corresponding merge base.
1132 1161 if bases[0] != nullrev:
1133 1162 base = bases[0]
@@ -1354,6 +1383,31 b' def abort(repo, originalwd, destmap, sta'
1354 1383 repo.ui.warn(_('rebase aborted\n'))
1355 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 1411 def buildstate(repo, destmap, collapse, obsoletenotrebased):
1358 1412 '''Define which revisions are going to be rebased and where
1359 1413
@@ -1372,12 +1426,21 b' def buildstate(repo, destmap, collapse, '
1372 1426 if set(destmap.values()) & mqapplied:
1373 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 1439 if not roots:
1377 1440 raise error.Abort(_('no matching revisions'))
1378 1441 roots.sort()
1379 1442 state = dict.fromkeys(rebaseset, revtodo)
1380 emptyrebase = True
1443 emptyrebase = (len(sortedsrc) == 1)
1381 1444 for root in roots:
1382 1445 dest = repo[destmap[root.rev()]]
1383 1446 commonbase = root.ancestor(dest)
@@ -204,7 +204,7 b' Destination is an ancestor of source:'
204 204 > |
205 205 > Z
206 206 > EOS
207 abort: source is ancestor of destination
207 abort: source and destination form a cycle
208 208 [255]
209 209
210 210 Switch roots:
@@ -291,22 +291,163 b' Move to a previous parent:'
291 291 |/
292 292 o 0: A
293 293
294 Source overlaps with destination (not handled well currently):
294 Source overlaps with destination:
295 295
296 296 $ rebasewithdag -s 'B+C+D' -d 'map(SRC, "B:C,C:D")' <<'EOS'
297 297 > B C D
298 298 > \|/
299 299 > A
300 300 > EOS
301 rebasing 2:dc0947a82db8 "C" (C)
301 302 rebasing 1:112478962961 "B" (B)
302 rebasing 2:dc0947a82db8 "C" (C)
303 o 5: C
303 o 5: B
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 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 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 245 @ 0: 'A'
246 246
247 247 $ hg rebase -s 5 -d 6
248 abort: source is ancestor of destination
248 abort: source and destination form a cycle
249 249 [255]
250 250
251 251 $ hg rebase -s 6 -d 5
@@ -264,7 +264,7 b' G onto F - rebase onto an ancestor:'
264 264 F onto G - rebase onto a descendant:
265 265
266 266 $ hg rebase -s 5 -d 6
267 abort: source is ancestor of destination
267 abort: source and destination form a cycle
268 268 [255]
269 269
270 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