Details
Details
- Reviewers
yuja - Group Reviewers
hg-reviewers - Commits
- rHGdd11df900f7f: rebase: replace --inmemory flag with rebase.experimental.inmemory config
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Lint
Lint Skipped - Unit
Unit Tests Skipped
( )
yuja |
hg-reviewers |
Lint Skipped |
Unit Tests Skipped |
Path | Packages | |||
---|---|---|---|---|
M | hgext/rebase.py (44 lines) | |||
M | mercurial/configitems.py (3 lines) | |||
M | tests/test-rebase-inmemory.t (10 lines) |
continue | continue | ||||
names.extend(ns.names(repo, ctx.node())) | names.extend(ns.names(repo, ctx.node())) | ||||
if names: | if names: | ||||
desc += ' (%s)' % ' '.join(names) | desc += ' (%s)' % ' '.join(names) | ||||
return desc | return desc | ||||
class rebaseruntime(object): | class rebaseruntime(object): | ||||
"""This class is a container for rebase runtime state""" | """This class is a container for rebase runtime state""" | ||||
def __init__(self, repo, ui, opts=None): | def __init__(self, repo, ui, inmemory=False, opts=None): | ||||
if opts is None: | if opts is None: | ||||
opts = {} | opts = {} | ||||
# prepared: whether we have rebasestate prepared or not. Currently it | # prepared: whether we have rebasestate prepared or not. Currently it | ||||
# decides whether "self.repo" is unfiltered or not. | # decides whether "self.repo" is unfiltered or not. | ||||
# The rebasestate has explicit hash to hash instructions not depending | # The rebasestate has explicit hash to hash instructions not depending | ||||
# on visibility. If rebasestate exists (in-memory or on-disk), use | # on visibility. If rebasestate exists (in-memory or on-disk), use | ||||
# unfiltered repo to avoid visibility issues. | # unfiltered repo to avoid visibility issues. | ||||
self.keepf = opts.get('keep', False) | self.keepf = opts.get('keep', False) | ||||
self.keepbranchesf = opts.get('keepbranches', False) | self.keepbranchesf = opts.get('keepbranches', False) | ||||
# keepopen is not meant for use on the command line, but by | # keepopen is not meant for use on the command line, but by | ||||
# other extensions | # other extensions | ||||
self.keepopen = opts.get('keepopen', False) | self.keepopen = opts.get('keepopen', False) | ||||
self.obsoletenotrebased = {} | self.obsoletenotrebased = {} | ||||
self.obsoletewithoutsuccessorindestination = set() | self.obsoletewithoutsuccessorindestination = set() | ||||
self.inmemory = opts.get('inmemory', False) | self.inmemory = inmemory | ||||
@property | @property | ||||
def repo(self): | def repo(self): | ||||
if self.prepared: | if self.prepared: | ||||
return self._repo.unfiltered() | return self._repo.unfiltered() | ||||
else: | else: | ||||
return self._repo | return self._repo | ||||
('l', 'logfile', '', | ('l', 'logfile', '', | ||||
_('read collapse commit message from file'), _('FILE')), | _('read collapse commit message from file'), _('FILE')), | ||||
('k', 'keep', False, _('keep original changesets')), | ('k', 'keep', False, _('keep original changesets')), | ||||
('', 'keepbranches', False, _('keep original branch names')), | ('', 'keepbranches', False, _('keep original branch names')), | ||||
('D', 'detach', False, _('(DEPRECATED)')), | ('D', 'detach', False, _('(DEPRECATED)')), | ||||
('i', 'interactive', False, _('(DEPRECATED)')), | ('i', 'interactive', False, _('(DEPRECATED)')), | ||||
('t', 'tool', '', _('specify merge tool')), | ('t', 'tool', '', _('specify merge tool')), | ||||
('c', 'continue', False, _('continue an interrupted rebase')), | ('c', 'continue', False, _('continue an interrupted rebase')), | ||||
('', 'inmemory', False, _('run rebase in-memory (EXPERIMENTAL)')), | |||||
('a', 'abort', False, _('abort an interrupted rebase'))] + | ('a', 'abort', False, _('abort an interrupted rebase'))] + | ||||
cmdutil.formatteropts, | cmdutil.formatteropts, | ||||
_('[-s REV | -b REV] [-d REV] [OPTION]')) | _('[-s REV | -b REV] [-d REV] [OPTION]')) | ||||
def rebase(ui, repo, **opts): | def rebase(ui, repo, **opts): | ||||
"""move changeset (and descendants) to a different branch | """move changeset (and descendants) to a different branch | ||||
Rebase uses repeated merging to graft changesets from one part of | Rebase uses repeated merging to graft changesets from one part of | ||||
history (the source) onto another (the destination). This can be | history (the source) onto another (the destination). This can be | ||||
performance purposes, you can configure rebase to use a single transaction | performance purposes, you can configure rebase to use a single transaction | ||||
across the entire rebase. WARNING: This setting introduces a significant | across the entire rebase. WARNING: This setting introduces a significant | ||||
risk of losing the work you've done in a rebase if the rebase aborts | risk of losing the work you've done in a rebase if the rebase aborts | ||||
unexpectedly:: | unexpectedly:: | ||||
[rebase] | [rebase] | ||||
singletransaction = True | singletransaction = True | ||||
By default, rebase writes to the working copy, but you can configure it to | |||||
run in-memory for for better performance, and to allow it to run if the | |||||
working copy is dirty:: | |||||
[rebase] | |||||
experimental.inmemory = True | |||||
Return Values: | Return Values: | ||||
Returns 0 on success, 1 if nothing to rebase or there are | Returns 0 on success, 1 if nothing to rebase or there are | ||||
unresolved conflicts. | unresolved conflicts. | ||||
""" | """ | ||||
inmemory = ui.configbool('rebase', 'experimental.inmemory') | |||||
if opts.get('continue') or opts.get('abort'): | if opts.get('continue') or opts.get('abort'): | ||||
# in-memory rebase is not compatible with resuming rebases. | # in-memory rebase is not compatible with resuming rebases. | ||||
opts['inmemory'] = False | inmemory = False | ||||
if opts.get('inmemory', False): | if inmemory: | ||||
try: | try: | ||||
# in-memory merge doesn't support conflicts, so if we hit any, abort | # in-memory merge doesn't support conflicts, so if we hit any, abort | ||||
# and re-run as an on-disk merge. | # and re-run as an on-disk merge. | ||||
return _origrebase(ui, repo, **opts) | return _origrebase(ui, repo, inmemory=inmemory, **opts) | ||||
except error.InMemoryMergeConflictsError: | except error.InMemoryMergeConflictsError: | ||||
ui.warn(_('hit merge conflicts; re-running rebase without in-memory' | ui.warn(_('hit merge conflicts; re-running rebase without in-memory' | ||||
' merge\n')) | ' merge\n')) | ||||
_origrebase(ui, repo, **{'abort': True}) | _origrebase(ui, repo, **{'abort': True}) | ||||
opts['inmemory'] = False | return _origrebase(ui, repo, inmemory=False, **opts) | ||||
return _origrebase(ui, repo, **opts) | |||||
else: | else: | ||||
return _origrebase(ui, repo, **opts) | return _origrebase(ui, repo, **opts) | ||||
def _origrebase(ui, repo, **opts): | def _origrebase(ui, repo, inmemory=False, **opts): | ||||
opts = pycompat.byteskwargs(opts) | opts = pycompat.byteskwargs(opts) | ||||
if 'inmemory' not in opts: | rbsrt = rebaseruntime(repo, ui, inmemory, opts) | ||||
opts['inmemory'] = False | |||||
rbsrt = rebaseruntime(repo, ui, opts) | |||||
with repo.wlock(), repo.lock(): | with repo.wlock(), repo.lock(): | ||||
# Validate input and define rebasing points | # Validate input and define rebasing points | ||||
destf = opts.get('dest', None) | destf = opts.get('dest', None) | ||||
srcf = opts.get('source', None) | srcf = opts.get('source', None) | ||||
basef = opts.get('base', None) | basef = opts.get('base', None) | ||||
revf = opts.get('rev', []) | revf = opts.get('rev', []) | ||||
# search default destination in this space | # search default destination in this space | ||||
if contf: | if contf: | ||||
ms = mergemod.mergestate.read(repo) | ms = mergemod.mergestate.read(repo) | ||||
mergeutil.checkunresolved(ms) | mergeutil.checkunresolved(ms) | ||||
retcode = rbsrt._prepareabortorcontinue(abortf) | retcode = rbsrt._prepareabortorcontinue(abortf) | ||||
if retcode is not None: | if retcode is not None: | ||||
return retcode | return retcode | ||||
else: | else: | ||||
destmap = _definedestmap(ui, repo, destf, srcf, basef, revf, | destmap = _definedestmap(ui, repo, rbsrt, destf, srcf, basef, revf, | ||||
destspace=destspace, | destspace=destspace) | ||||
opts=opts) | |||||
rbsrt.inmemory = opts['inmemory'] | |||||
retcode = rbsrt._preparenewrebase(destmap) | retcode = rbsrt._preparenewrebase(destmap) | ||||
if retcode is not None: | if retcode is not None: | ||||
return retcode | return retcode | ||||
tr = None | tr = None | ||||
dsguard = None | dsguard = None | ||||
singletr = ui.configbool('rebase', 'singletransaction') | singletr = ui.configbool('rebase', 'singletransaction') | ||||
if singletr: | if singletr: | ||||
tr = repo.transaction('rebase') | tr = repo.transaction('rebase') | ||||
with util.acceptintervention(tr): | with util.acceptintervention(tr): | ||||
if singletr: | if singletr: | ||||
dsguard = dirstateguard.dirstateguard(repo, 'rebase') | dsguard = dirstateguard.dirstateguard(repo, 'rebase') | ||||
with util.acceptintervention(dsguard): | with util.acceptintervention(dsguard): | ||||
rbsrt._performrebase(tr) | rbsrt._performrebase(tr) | ||||
rbsrt._finishrebase() | rbsrt._finishrebase() | ||||
def _definedestmap(ui, repo, destf=None, srcf=None, basef=None, revf=None, | def _definedestmap(ui, repo, rbsrt, destf=None, srcf=None, basef=None, | ||||
destspace=None, opts=None): | revf=None, destspace=None): | ||||
"""use revisions argument to define destmap {srcrev: destrev}""" | """use revisions argument to define destmap {srcrev: destrev}""" | ||||
if revf is None: | if revf is None: | ||||
revf = [] | revf = [] | ||||
# destspace is here to work around issues with `hg pull --rebase` see | # destspace is here to work around issues with `hg pull --rebase` see | ||||
# issue5214 for details | # issue5214 for details | ||||
if srcf and basef: | if srcf and basef: | ||||
raise error.Abort(_('cannot specify both a source and a base')) | raise error.Abort(_('cannot specify both a source and a base')) | ||||
if revf and basef: | if revf and basef: | ||||
raise error.Abort(_('cannot specify both a revision and a base')) | raise error.Abort(_('cannot specify both a revision and a base')) | ||||
if revf and srcf: | if revf and srcf: | ||||
raise error.Abort(_('cannot specify both a revision and a source')) | raise error.Abort(_('cannot specify both a revision and a source')) | ||||
if not opts['inmemory']: | if not rbsrt.inmemory: | ||||
cmdutil.checkunfinished(repo) | cmdutil.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
if ui.configbool('commands', 'rebase.requiredest') and not destf: | if ui.configbool('commands', 'rebase.requiredest') and not destf: | ||||
raise error.Abort(_('you must specify a destination'), | raise error.Abort(_('you must specify a destination'), | ||||
hint=_('use: hg rebase -d REV')) | hint=_('use: hg rebase -d REV')) | ||||
dest = None | dest = None | ||||
# outweights the benefits of rebasing in-memory, and executing an extra | # outweights the benefits of rebasing in-memory, and executing an extra | ||||
# update command adds a bit of overhead, so better to just do it on disk. In | # update command adds a bit of overhead, so better to just do it on disk. In | ||||
# all other cases leave it on. | # all other cases leave it on. | ||||
# | # | ||||
# Note that there are cases where this isn't true -- e.g., rebasing large | # Note that there are cases where this isn't true -- e.g., rebasing large | ||||
# stacks that include the WCP. However, I'm not yet sure where the cutoff | # stacks that include the WCP. However, I'm not yet sure where the cutoff | ||||
# is. | # is. | ||||
rebasingwcp = repo['.'].rev() in rebaseset | rebasingwcp = repo['.'].rev() in rebaseset | ||||
if opts['inmemory'] and rebasingwcp: | if rbsrt.inmemory and rebasingwcp: | ||||
opts['inmemory'] = False | rbsrt.inmemory = False | ||||
# Check these since we did not before. | # Check these since we did not before. | ||||
cmdutil.checkunfinished(repo) | cmdutil.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
if not destf: | if not destf: | ||||
dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] | dest = repo[_destrebase(repo, rebaseset, destspace=destspace)] | ||||
destf = str(dest) | destf = str(dest) | ||||
default=False, | default=False, | ||||
) | ) | ||||
coreconfigitem('experimental', 'rebaseskipobsolete', | coreconfigitem('experimental', 'rebaseskipobsolete', | ||||
default=True, | default=True, | ||||
) | ) | ||||
coreconfigitem('rebase', 'singletransaction', | coreconfigitem('rebase', 'singletransaction', | ||||
default=False, | default=False, | ||||
) | ) | ||||
coreconfigitem('rebase', 'experimental.inmemory', | |||||
default=False, | |||||
) |
#require symlink execbit | #require symlink execbit | ||||
$ cat << EOF >> $HGRCPATH | $ cat << EOF >> $HGRCPATH | ||||
> [extensions] | > [extensions] | ||||
> amend= | > amend= | ||||
> rebase= | > rebase= | ||||
> debugdrawdag=$TESTDIR/drawdag.py | > debugdrawdag=$TESTDIR/drawdag.py | ||||
> [rebase] | |||||
> experimental.inmemory=1 | |||||
> [diff] | > [diff] | ||||
> git=1 | > git=1 | ||||
> [alias] | > [alias] | ||||
> tglog = log -G --template "{rev}: {node|short} '{desc}'\n" | > tglog = log -G --template "{rev}: {node|short} '{desc}'\n" | ||||
> EOF | > EOF | ||||
Rebase a simple DAG: | Rebase a simple DAG: | ||||
$ hg init repo1 | $ hg init repo1 | ||||
o 1: 02952614a83d 'd' | o 1: 02952614a83d 'd' | ||||
| | | | ||||
@ 0: b173517d0057 'a' | @ 0: b173517d0057 'a' | ||||
$ hg cat -r 3 c | $ hg cat -r 3 c | ||||
c (no-eol) | c (no-eol) | ||||
$ hg cat -r 2 b | $ hg cat -r 2 b | ||||
b (no-eol) | b (no-eol) | ||||
$ hg rebase --inmemory --debug -r b -d c | grep rebasing | $ hg rebase --debug -r b -d c | grep rebasing | ||||
rebasing in-memory | rebasing in-memory | ||||
rebasing 2:db0e82a16a62 "b" (b) | rebasing 2:db0e82a16a62 "b" (b) | ||||
$ hg tglog | $ hg tglog | ||||
o 3: ca58782ad1e4 'b' | o 3: ca58782ad1e4 'b' | ||||
| | | | ||||
o 2: 814f6bd05178 'c' | o 2: 814f6bd05178 'c' | ||||
| | | | ||||
o 1: 02952614a83d 'd' | o 1: 02952614a83d 'd' | ||||
@ 0: b173517d0057 'a' | @ 0: b173517d0057 'a' | ||||
$ hg cat -r 3 c | $ hg cat -r 3 c | ||||
c (no-eol) | c (no-eol) | ||||
$ hg cat -r 2 b | $ hg cat -r 2 b | ||||
b (no-eol) | b (no-eol) | ||||
$ hg cat -r 3 e | $ hg cat -r 3 e | ||||
somefile (no-eol) | somefile (no-eol) | ||||
$ hg rebase --inmemory --debug -s b -d a | grep rebasing | $ hg rebase --debug -s b -d a | grep rebasing | ||||
rebasing in-memory | rebasing in-memory | ||||
rebasing 2:db0e82a16a62 "b" (b) | rebasing 2:db0e82a16a62 "b" (b) | ||||
$ hg tglog | $ hg tglog | ||||
o 3: fc055c3b4d33 'b' | o 3: fc055c3b4d33 'b' | ||||
| | | | ||||
| o 2: f56b71190a8f 'c' | | o 2: f56b71190a8f 'c' | ||||
| | | | | | ||||
| o 1: 02952614a83d 'd' | | o 1: 02952614a83d 'd' | ||||
|/ | |/ | ||||
@ 0: b173517d0057 'a' | @ 0: b173517d0057 'a' | ||||
$ hg cat -r 2 c | $ hg cat -r 2 c | ||||
c (no-eol) | c (no-eol) | ||||
$ hg cat -r 3 b | $ hg cat -r 3 b | ||||
b (no-eol) | b (no-eol) | ||||
$ hg rebase --inmemory --debug -s 1 -d 3 | grep rebasing | $ hg rebase --debug -s 1 -d 3 | grep rebasing | ||||
rebasing in-memory | rebasing in-memory | ||||
rebasing 1:02952614a83d "d" (d) | rebasing 1:02952614a83d "d" (d) | ||||
rebasing 2:f56b71190a8f "c" | rebasing 2:f56b71190a8f "c" | ||||
$ hg tglog | $ hg tglog | ||||
o 3: 753feb6fd12a 'c' | o 3: 753feb6fd12a 'c' | ||||
| | | | ||||
o 2: 09c044d2cb43 'd' | o 2: 09c044d2cb43 'd' | ||||
| | | | ||||
e -> somefile | e -> somefile | ||||
$ ls -l f | cut -c -10 | $ ls -l f | cut -c -10 | ||||
-rwxr-xr-x | -rwxr-xr-x | ||||
Rebase the working copy parent, which should default to an on-disk merge even if | Rebase the working copy parent, which should default to an on-disk merge even if | ||||
we requested in-memory. | we requested in-memory. | ||||
$ hg up -C 3 | $ hg up -C 3 | ||||
0 files updated, 0 files merged, 0 files removed, 0 files unresolved | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
$ hg rebase -r 3 -d 0 --inmemory --debug | grep rebasing | $ hg rebase -r 3 -d 0 --debug | grep rebasing | ||||
rebasing on disk | rebasing on disk | ||||
rebasing 3:753feb6fd12a "c" (tip) | rebasing 3:753feb6fd12a "c" (tip) | ||||
$ hg tglog | $ hg tglog | ||||
@ 3: 844a7de3e617 'c' | @ 3: 844a7de3e617 'c' | ||||
| | | | ||||
| o 2: 09c044d2cb43 'd' | | o 2: 09c044d2cb43 'd' | ||||
| | | | | | ||||
| o 1: fc055c3b4d33 'b' | | o 1: fc055c3b4d33 'b' | ||||
|/ | |/ | ||||
o 0: b173517d0057 'a' | o 0: b173517d0057 'a' | ||||