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