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 |
|
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 = |
|
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 |
|
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 |
|
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: |
|
353 | | o 4: E | |
|
354 | | |\ | |||
|
355 | +---o 2: C | |||
308 | | | |
|
356 | | | | |
309 |
| |
|
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 |
|
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 |
|
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