##// END OF EJS Templates
rebase: remove trailing whitespace found by check-code
Augie Fackler -
r14917:2957b8b1 stable
parent child Browse files
Show More
@@ -1,618 +1,618 b''
1 1 # rebase.py - rebasing feature for mercurial
2 2 #
3 3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to move sets of revisions to a different ancestor
9 9
10 10 This extension lets you rebase changesets in an existing Mercurial
11 11 repository.
12 12
13 13 For more information:
14 14 http://mercurial.selenic.com/wiki/RebaseExtension
15 15 '''
16 16
17 17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 18 from mercurial import extensions, copies, patch
19 19 from mercurial.commands import templateopts
20 20 from mercurial.node import nullrev
21 21 from mercurial.lock import release
22 22 from mercurial.i18n import _
23 23 import os, errno
24 24
25 25 nullmerge = -2
26 26
27 27 cmdtable = {}
28 28 command = cmdutil.command(cmdtable)
29 29
30 30 @command('rebase',
31 31 [('s', 'source', '',
32 32 _('rebase from the specified changeset'), _('REV')),
33 33 ('b', 'base', '',
34 34 _('rebase from the base of the specified changeset '
35 35 '(up to greatest common ancestor of base and dest)'),
36 36 _('REV')),
37 37 ('d', 'dest', '',
38 38 _('rebase onto the specified changeset'), _('REV')),
39 39 ('', 'collapse', False, _('collapse the rebased changesets')),
40 40 ('m', 'message', '',
41 41 _('use text as collapse commit message'), _('TEXT')),
42 42 ('l', 'logfile', '',
43 43 _('read collapse commit message from file'), _('FILE')),
44 44 ('', 'keep', False, _('keep original changesets')),
45 45 ('', 'keepbranches', False, _('keep original branch names')),
46 46 ('', 'detach', False, _('force detaching of source from its original '
47 47 'branch')),
48 48 ('t', 'tool', '', _('specify merge tool')),
49 49 ('c', 'continue', False, _('continue an interrupted rebase')),
50 50 ('a', 'abort', False, _('abort an interrupted rebase'))] +
51 51 templateopts,
52 52 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
53 53 'hg rebase {-a|-c}'))
54 54 def rebase(ui, repo, **opts):
55 55 """move changeset (and descendants) to a different branch
56 56
57 57 Rebase uses repeated merging to graft changesets from one part of
58 58 history (the source) onto another (the destination). This can be
59 59 useful for linearizing *local* changes relative to a master
60 60 development tree.
61 61
62 62 You should not rebase changesets that have already been shared
63 63 with others. Doing so will force everybody else to perform the
64 64 same rebase or they will end up with duplicated changesets after
65 65 pulling in your rebased changesets.
66 66
67 67 If you don't specify a destination changeset (``-d/--dest``),
68 68 rebase uses the tipmost head of the current named branch as the
69 69 destination. (The destination changeset is not modified by
70 70 rebasing, but new changesets are added as its descendants.)
71 71
72 72 You can specify which changesets to rebase in two ways: as a
73 73 "source" changeset or as a "base" changeset. Both are shorthand
74 74 for a topologically related set of changesets (the "source
75 75 branch"). If you specify source (``-s/--source``), rebase will
76 76 rebase that changeset and all of its descendants onto dest. If you
77 77 specify base (``-b/--base``), rebase will select ancestors of base
78 78 back to but not including the common ancestor with dest. Thus,
79 79 ``-b`` is less precise but more convenient than ``-s``: you can
80 80 specify any changeset in the source branch, and rebase will select
81 81 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
82 82 uses the parent of the working directory as the base.
83 83
84 84 By default, rebase recreates the changesets in the source branch
85 85 as descendants of dest and then destroys the originals. Use
86 86 ``--keep`` to preserve the original source changesets. Some
87 87 changesets in the source branch (e.g. merges from the destination
88 88 branch) may be dropped if they no longer contribute any change.
89 89
90 90 One result of the rules for selecting the destination changeset
91 91 and source branch is that, unlike ``merge``, rebase will do
92 92 nothing if you are at the latest (tipmost) head of a named branch
93 93 with two heads. You need to explicitly specify source and/or
94 94 destination (or ``update`` to the other head, if it's the head of
95 95 the intended source branch).
96 96
97 97 If a rebase is interrupted to manually resolve a merge, it can be
98 98 continued with --continue/-c or aborted with --abort/-a.
99 99
100 100 Returns 0 on success, 1 if nothing to rebase.
101 101 """
102 102 originalwd = target = None
103 103 external = nullrev
104 104 state = {}
105 105 skipped = set()
106 106 targetancestors = set()
107 107
108 108 lock = wlock = None
109 109 try:
110 110 lock = repo.lock()
111 111 wlock = repo.wlock()
112 112
113 113 # Validate input and define rebasing points
114 114 destf = opts.get('dest', None)
115 115 srcf = opts.get('source', None)
116 116 basef = opts.get('base', None)
117 117 contf = opts.get('continue')
118 118 abortf = opts.get('abort')
119 119 collapsef = opts.get('collapse', False)
120 120 collapsemsg = cmdutil.logmessage(ui, opts)
121 121 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
122 122 keepf = opts.get('keep', False)
123 123 keepbranchesf = opts.get('keepbranches', False)
124 124 detachf = opts.get('detach', False)
125 125 # keepopen is not meant for use on the command line, but by
126 126 # other extensions
127 127 keepopen = opts.get('keepopen', False)
128 128
129 129 if collapsemsg and not collapsef:
130 130 raise util.Abort(
131 131 _('message can only be specified with collapse'))
132 132
133 133 if contf or abortf:
134 134 if contf and abortf:
135 135 raise util.Abort(_('cannot use both abort and continue'))
136 136 if collapsef:
137 137 raise util.Abort(
138 138 _('cannot use collapse with continue or abort'))
139 139 if detachf:
140 140 raise util.Abort(_('cannot use detach with continue or abort'))
141 141 if srcf or basef or destf:
142 142 raise util.Abort(
143 143 _('abort and continue do not allow specifying revisions'))
144 144 if opts.get('tool', False):
145 145 ui.warn(_('tool option will be ignored\n'))
146 146
147 147 (originalwd, target, state, skipped, collapsef, keepf,
148 148 keepbranchesf, external) = restorestatus(repo)
149 149 if abortf:
150 150 return abort(repo, originalwd, target, state)
151 151 else:
152 152 if srcf and basef:
153 153 raise util.Abort(_('cannot specify both a '
154 154 'revision and a base'))
155 155 if detachf:
156 156 if not srcf:
157 157 raise util.Abort(
158 158 _('detach requires a revision to be specified'))
159 159 if basef:
160 160 raise util.Abort(_('cannot specify a base with detach'))
161 161
162 162 cmdutil.bailifchanged(repo)
163 163 result = buildstate(repo, destf, srcf, basef, detachf)
164 164 if not result:
165 165 # Empty state built, nothing to rebase
166 166 ui.status(_('nothing to rebase\n'))
167 167 return 1
168 168 else:
169 169 originalwd, target, state = result
170 170 if collapsef:
171 171 targetancestors = set(repo.changelog.ancestors(target))
172 172 external = checkexternal(repo, state, targetancestors)
173 173
174 174 if keepbranchesf:
175 175 assert not extrafn, 'cannot use both keepbranches and extrafn'
176 176 def extrafn(ctx, extra):
177 177 extra['branch'] = ctx.branch()
178 178 if collapsef:
179 179 branches = set()
180 180 for rev in state:
181 181 branches.add(repo[rev].branch())
182 182 if len(branches) > 1:
183 raise util.Abort(_('cannot collapse multiple named '
183 raise util.Abort(_('cannot collapse multiple named '
184 184 'branches'))
185 185
186 186
187 187 # Rebase
188 188 if not targetancestors:
189 189 targetancestors = set(repo.changelog.ancestors(target))
190 190 targetancestors.add(target)
191 191
192 192 # Keep track of the current bookmarks in order to reset them later
193 193 currentbookmarks = repo._bookmarks.copy()
194 194
195 195 sortedstate = sorted(state)
196 196 total = len(sortedstate)
197 197 pos = 0
198 198 for rev in sortedstate:
199 199 pos += 1
200 200 if state[rev] == -1:
201 201 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
202 202 _('changesets'), total)
203 203 storestatus(repo, originalwd, target, state, collapsef, keepf,
204 204 keepbranchesf, external)
205 205 p1, p2 = defineparents(repo, rev, target, state,
206 206 targetancestors)
207 207 if len(repo.parents()) == 2:
208 208 repo.ui.debug('resuming interrupted rebase\n')
209 209 else:
210 210 try:
211 211 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
212 212 stats = rebasenode(repo, rev, p1, state)
213 213 if stats and stats[3] > 0:
214 214 raise util.Abort(_('unresolved conflicts (see hg '
215 215 'resolve, then hg rebase --continue)'))
216 216 finally:
217 217 ui.setconfig('ui', 'forcemerge', '')
218 218 updatedirstate(repo, rev, target, p2)
219 219 if not collapsef:
220 220 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
221 221 else:
222 222 # Skip commit if we are collapsing
223 223 repo.dirstate.setparents(repo[p1].node())
224 224 newrev = None
225 225 # Update the state
226 226 if newrev is not None:
227 227 state[rev] = repo[newrev].rev()
228 228 else:
229 229 if not collapsef:
230 230 ui.note(_('no changes, revision %d skipped\n') % rev)
231 231 ui.debug('next revision set to %s\n' % p1)
232 232 skipped.add(rev)
233 233 state[rev] = p1
234 234
235 235 ui.progress(_('rebasing'), None)
236 236 ui.note(_('rebase merging completed\n'))
237 237
238 238 if collapsef and not keepopen:
239 239 p1, p2 = defineparents(repo, min(state), target,
240 240 state, targetancestors)
241 241 if collapsemsg:
242 242 commitmsg = collapsemsg
243 243 else:
244 244 commitmsg = 'Collapsed revision'
245 245 for rebased in state:
246 246 if rebased not in skipped and state[rebased] != nullmerge:
247 247 commitmsg += '\n* %s' % repo[rebased].description()
248 248 commitmsg = ui.edit(commitmsg, repo.ui.username())
249 249 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
250 250 extrafn=extrafn)
251 251
252 252 if 'qtip' in repo.tags():
253 253 updatemq(repo, state, skipped, **opts)
254 254
255 255 if currentbookmarks:
256 256 # Nodeids are needed to reset bookmarks
257 257 nstate = {}
258 258 for k, v in state.iteritems():
259 259 if v != nullmerge:
260 260 nstate[repo[k].node()] = repo[v].node()
261 261
262 262 if not keepf:
263 263 # Remove no more useful revisions
264 264 rebased = [rev for rev in state if state[rev] != nullmerge]
265 265 if rebased:
266 266 if set(repo.changelog.descendants(min(rebased))) - set(state):
267 267 ui.warn(_("warning: new changesets detected "
268 268 "on source branch, not stripping\n"))
269 269 else:
270 270 # backup the old csets by default
271 271 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
272 272
273 273 if currentbookmarks:
274 274 updatebookmarks(repo, nstate, currentbookmarks, **opts)
275 275
276 276 clearstatus(repo)
277 277 ui.note(_("rebase completed\n"))
278 278 if os.path.exists(repo.sjoin('undo')):
279 279 util.unlinkpath(repo.sjoin('undo'))
280 280 if skipped:
281 281 ui.note(_("%d revisions have been skipped\n") % len(skipped))
282 282 finally:
283 283 release(lock, wlock)
284 284
285 285 def checkexternal(repo, state, targetancestors):
286 286 """Check whether one or more external revisions need to be taken in
287 287 consideration. In the latter case, abort.
288 288 """
289 289 external = nullrev
290 290 source = min(state)
291 291 for rev in state:
292 292 if rev == source:
293 293 continue
294 294 # Check externals and fail if there are more than one
295 295 for p in repo[rev].parents():
296 296 if (p.rev() not in state
297 297 and p.rev() not in targetancestors):
298 298 if external != nullrev:
299 299 raise util.Abort(_('unable to collapse, there is more '
300 300 'than one external parent'))
301 301 external = p.rev()
302 302 return external
303 303
304 304 def updatedirstate(repo, rev, p1, p2):
305 305 """Keep track of renamed files in the revision that is going to be rebased
306 306 """
307 307 # Here we simulate the copies and renames in the source changeset
308 308 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
309 309 m1 = repo[rev].manifest()
310 310 m2 = repo[p1].manifest()
311 311 for k, v in cop.iteritems():
312 312 if k in m1:
313 313 if v in m1 or v in m2:
314 314 repo.dirstate.copy(v, k)
315 315 if v in m2 and v not in m1 and k in m2:
316 316 repo.dirstate.remove(v)
317 317
318 318 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
319 319 'Commit the changes and store useful information in extra'
320 320 try:
321 321 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
322 322 ctx = repo[rev]
323 323 if commitmsg is None:
324 324 commitmsg = ctx.description()
325 325 extra = {'rebase_source': ctx.hex()}
326 326 if extrafn:
327 327 extrafn(ctx, extra)
328 328 # Commit might fail if unresolved files exist
329 329 newrev = repo.commit(text=commitmsg, user=ctx.user(),
330 330 date=ctx.date(), extra=extra)
331 331 repo.dirstate.setbranch(repo[newrev].branch())
332 332 return newrev
333 333 except util.Abort:
334 334 # Invalidate the previous setparents
335 335 repo.dirstate.invalidate()
336 336 raise
337 337
338 338 def rebasenode(repo, rev, p1, state):
339 339 'Rebase a single revision'
340 340 # Merge phase
341 341 # Update to target and merge it with local
342 342 if repo['.'].rev() != repo[p1].rev():
343 343 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
344 344 merge.update(repo, p1, False, True, False)
345 345 else:
346 346 repo.ui.debug(" already in target\n")
347 347 repo.dirstate.write()
348 348 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
349 349 base = None
350 350 if repo[rev].rev() != repo[min(state)].rev():
351 351 base = repo[rev].p1().node()
352 352 return merge.update(repo, rev, True, True, False, base)
353 353
354 354 def defineparents(repo, rev, target, state, targetancestors):
355 355 'Return the new parent relationship of the revision that will be rebased'
356 356 parents = repo[rev].parents()
357 357 p1 = p2 = nullrev
358 358
359 359 P1n = parents[0].rev()
360 360 if P1n in targetancestors:
361 361 p1 = target
362 362 elif P1n in state:
363 363 if state[P1n] == nullmerge:
364 364 p1 = target
365 365 else:
366 366 p1 = state[P1n]
367 367 else: # P1n external
368 368 p1 = target
369 369 p2 = P1n
370 370
371 371 if len(parents) == 2 and parents[1].rev() not in targetancestors:
372 372 P2n = parents[1].rev()
373 373 # interesting second parent
374 374 if P2n in state:
375 375 if p1 == target: # P1n in targetancestors or external
376 376 p1 = state[P2n]
377 377 else:
378 378 p2 = state[P2n]
379 379 else: # P2n external
380 380 if p2 != nullrev: # P1n external too => rev is a merged revision
381 381 raise util.Abort(_('cannot use revision %d as base, result '
382 382 'would have 3 parents') % rev)
383 383 p2 = P2n
384 384 repo.ui.debug(" future parents are %d and %d\n" %
385 385 (repo[p1].rev(), repo[p2].rev()))
386 386 return p1, p2
387 387
388 388 def isagitpatch(repo, patchname):
389 389 'Return true if the given patch is in git format'
390 390 mqpatch = os.path.join(repo.mq.path, patchname)
391 391 for line in patch.linereader(file(mqpatch, 'rb')):
392 392 if line.startswith('diff --git'):
393 393 return True
394 394 return False
395 395
396 396 def updatemq(repo, state, skipped, **opts):
397 397 'Update rebased mq patches - finalize and then import them'
398 398 mqrebase = {}
399 399 mq = repo.mq
400 400 original_series = mq.fullseries[:]
401 401
402 402 for p in mq.applied:
403 403 rev = repo[p.node].rev()
404 404 if rev in state:
405 405 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
406 406 (rev, p.name))
407 407 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
408 408
409 409 if mqrebase:
410 410 mq.finish(repo, mqrebase.keys())
411 411
412 412 # We must start import from the newest revision
413 413 for rev in sorted(mqrebase, reverse=True):
414 414 if rev not in skipped:
415 415 name, isgit = mqrebase[rev]
416 416 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
417 417 mq.qimport(repo, (), patchname=name, git=isgit,
418 418 rev=[str(state[rev])])
419 419
420 420 # restore old series to preserve guards
421 421 mq.fullseries = original_series
422 422 mq.series_dirty = True
423 423 mq.savedirty()
424 424
425 425 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
426 426 'Move bookmarks to their correct changesets'
427 427 current = repo._bookmarkcurrent
428 428 for k, v in originalbookmarks.iteritems():
429 429 if v in nstate:
430 430 if nstate[v] != nullmerge:
431 431 # reset the pointer if the bookmark was moved incorrectly
432 432 if k != current:
433 433 repo._bookmarks[k] = nstate[v]
434 434
435 435 bookmarks.write(repo)
436 436
437 437 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
438 438 external):
439 439 'Store the current status to allow recovery'
440 440 f = repo.opener("rebasestate", "w")
441 441 f.write(repo[originalwd].hex() + '\n')
442 442 f.write(repo[target].hex() + '\n')
443 443 f.write(repo[external].hex() + '\n')
444 444 f.write('%d\n' % int(collapse))
445 445 f.write('%d\n' % int(keep))
446 446 f.write('%d\n' % int(keepbranches))
447 447 for d, v in state.iteritems():
448 448 oldrev = repo[d].hex()
449 449 newrev = repo[v].hex()
450 450 f.write("%s:%s\n" % (oldrev, newrev))
451 451 f.close()
452 452 repo.ui.debug('rebase status stored\n')
453 453
454 454 def clearstatus(repo):
455 455 'Remove the status files'
456 456 if os.path.exists(repo.join("rebasestate")):
457 457 util.unlinkpath(repo.join("rebasestate"))
458 458
459 459 def restorestatus(repo):
460 460 'Restore a previously stored status'
461 461 try:
462 462 target = None
463 463 collapse = False
464 464 external = nullrev
465 465 state = {}
466 466 f = repo.opener("rebasestate")
467 467 for i, l in enumerate(f.read().splitlines()):
468 468 if i == 0:
469 469 originalwd = repo[l].rev()
470 470 elif i == 1:
471 471 target = repo[l].rev()
472 472 elif i == 2:
473 473 external = repo[l].rev()
474 474 elif i == 3:
475 475 collapse = bool(int(l))
476 476 elif i == 4:
477 477 keep = bool(int(l))
478 478 elif i == 5:
479 479 keepbranches = bool(int(l))
480 480 else:
481 481 oldrev, newrev = l.split(':')
482 482 state[repo[oldrev].rev()] = repo[newrev].rev()
483 483 skipped = set()
484 484 # recompute the set of skipped revs
485 485 if not collapse:
486 486 seen = set([target])
487 487 for old, new in sorted(state.items()):
488 488 if new != nullrev and new in seen:
489 489 skipped.add(old)
490 490 seen.add(new)
491 491 repo.ui.debug('computed skipped revs: %s\n' % skipped)
492 492 repo.ui.debug('rebase status resumed\n')
493 493 return (originalwd, target, state, skipped,
494 494 collapse, keep, keepbranches, external)
495 495 except IOError, err:
496 496 if err.errno != errno.ENOENT:
497 497 raise
498 498 raise util.Abort(_('no rebase in progress'))
499 499
500 500 def abort(repo, originalwd, target, state):
501 501 'Restore the repository to its original state'
502 502 if set(repo.changelog.descendants(target)) - set(state.values()):
503 503 repo.ui.warn(_("warning: new changesets detected on target branch, "
504 504 "can't abort\n"))
505 505 return -1
506 506 else:
507 507 # Strip from the first rebased revision
508 508 merge.update(repo, repo[originalwd].rev(), False, True, False)
509 509 rebased = filter(lambda x: x > -1 and x != target, state.values())
510 510 if rebased:
511 511 strippoint = min(rebased)
512 512 # no backup of rebased cset versions needed
513 513 repair.strip(repo.ui, repo, repo[strippoint].node())
514 514 clearstatus(repo)
515 515 repo.ui.warn(_('rebase aborted\n'))
516 516 return 0
517 517
518 518 def buildstate(repo, dest, src, base, detach):
519 519 'Define which revisions are going to be rebased and where'
520 520 targetancestors = set()
521 521 detachset = set()
522 522
523 523 if not dest:
524 524 # Destination defaults to the latest revision in the current branch
525 525 branch = repo[None].branch()
526 526 dest = repo[branch].rev()
527 527 else:
528 528 dest = repo[dest].rev()
529 529
530 530 # This check isn't strictly necessary, since mq detects commits over an
531 531 # applied patch. But it prevents messing up the working directory when
532 532 # a partially completed rebase is blocked by mq.
533 533 if 'qtip' in repo.tags() and (repo[dest].node() in
534 534 [s.node for s in repo.mq.applied]):
535 535 raise util.Abort(_('cannot rebase onto an applied mq patch'))
536 536
537 537 if src:
538 538 commonbase = repo[src].ancestor(repo[dest])
539 539 samebranch = repo[src].branch() == repo[dest].branch()
540 540 if commonbase == repo[src]:
541 541 raise util.Abort(_('source is ancestor of destination'))
542 542 if samebranch and commonbase == repo[dest]:
543 543 raise util.Abort(_('source is descendant of destination'))
544 544 source = repo[src].rev()
545 545 if detach:
546 546 # We need to keep track of source's ancestors up to the common base
547 547 srcancestors = set(repo.changelog.ancestors(source))
548 548 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
549 549 detachset = srcancestors - baseancestors
550 550 detachset.discard(commonbase.rev())
551 551 else:
552 552 if base:
553 553 cwd = repo[base].rev()
554 554 else:
555 555 cwd = repo['.'].rev()
556 556
557 557 if cwd == dest:
558 558 repo.ui.debug('source and destination are the same\n')
559 559 return None
560 560
561 561 targetancestors = set(repo.changelog.ancestors(dest))
562 562 if cwd in targetancestors:
563 563 repo.ui.debug('source is ancestor of destination\n')
564 564 return None
565 565
566 566 cwdancestors = set(repo.changelog.ancestors(cwd))
567 567 if dest in cwdancestors:
568 568 repo.ui.debug('source is descendant of destination\n')
569 569 return None
570 570
571 571 cwdancestors.add(cwd)
572 572 rebasingbranch = cwdancestors - targetancestors
573 573 source = min(rebasingbranch)
574 574
575 575 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
576 576 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
577 577 state.update(dict.fromkeys(detachset, nullmerge))
578 578 state[source] = nullrev
579 579 return repo['.'].rev(), repo[dest].rev(), state
580 580
581 581 def pullrebase(orig, ui, repo, *args, **opts):
582 582 'Call rebase after pull if the latter has been invoked with --rebase'
583 583 if opts.get('rebase'):
584 584 if opts.get('update'):
585 585 del opts['update']
586 586 ui.debug('--update and --rebase are not compatible, ignoring '
587 587 'the update flag\n')
588 588
589 589 cmdutil.bailifchanged(repo)
590 590 revsprepull = len(repo)
591 591 origpostincoming = commands.postincoming
592 592 def _dummy(*args, **kwargs):
593 593 pass
594 594 commands.postincoming = _dummy
595 595 try:
596 596 orig(ui, repo, *args, **opts)
597 597 finally:
598 598 commands.postincoming = origpostincoming
599 599 revspostpull = len(repo)
600 600 if revspostpull > revsprepull:
601 601 rebase(ui, repo, **opts)
602 602 branch = repo[None].branch()
603 603 dest = repo[branch].rev()
604 604 if dest != repo['.'].rev():
605 605 # there was nothing to rebase we force an update
606 606 hg.update(repo, dest)
607 607 else:
608 608 if opts.get('tool'):
609 609 raise util.Abort(_('--tool can only be used with --rebase'))
610 610 orig(ui, repo, *args, **opts)
611 611
612 612 def uisetup(ui):
613 613 'Replace pull with a decorator to provide --rebase option'
614 614 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
615 615 entry[1].append(('', 'rebase', None,
616 616 _("rebase working directory to branch head")))
617 617 entry[1].append(('t', 'tool', '',
618 618 _("specify merge tool for rebase")))
General Comments 0
You need to be logged in to leave comments. Login now