If you don't want to store any backup while rebasing, you can
use history-editing-backup config option.
[ui]
history-editing-backup = # True or False
Current status of list of commands which supports this config:
- histedit
- rebase
hg-reviewers |
If you don't want to store any backup while rebasing, you can
use history-editing-backup config option.
[ui]
history-editing-backup = # True or False
Current status of list of commands which supports this config:
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
Added --no-backup option which gives functionality to not save backup copies of files.
Can you elaborate why we want this option?
I'm -0 on this because it's unclear when the --no-backup option makes a
difference. --abort isn't the only way to strip revisions while rebasing.
In D3887#60749, @yuja wrote:Added --no-backup option which gives functionality to not save backup copies of files.Can you elaborate why we want this option?
The idea was to add --no-backup option in all those commands which alter history. For this one, let say if someone wants to abort a rebase in between then a --no-backup option should be available (same as histedit)
I'm -0 on this because it's unclear when the --no-backup option makes a
difference. --abort isn't the only way to strip revisions while rebasing.
Oh, I thought --abort is the only way to strip revisions. Shall I try to cover all other cases also where we strip csets; Or If it is not required I can leave it here.
The idea was to add --no-backup option in all those commands which alter
history. For this one, let say if someone wants to abort a rebase in between
then a --no-backup option should be available (same as histedit)
If he don't want any backup bundle on history editing, it might be better
to add a config knob to turn off the feature than forcing him to always
specify --no-backup.
histedit and rebase are different from strip in that the user doesn't intend
to "remove" changesets.
Oh, I thought --abort is the only way to strip revisions.
Pre-obsmarker rebase will strip rebased revisions.
Shall I try to cover all other cases also where we strip csets; Or If it is
not required I can leave it here.
I don't have strong opinion about that, but it's probably better to add a
test with some comment saying --no-backup doesn't work and should be fixed
later.
In D3887#60752, @yuja wrote:The idea was to add --no-backup option in all those commands which alter
history. For this one, let say if someone wants to abort a rebase in between
then a --no-backup option should be available (same as histedit)If he don't want any backup bundle on history editing, it might be better
to add a config knob to turn off the feature than forcing him to always
specify --no-backup.
I agree with Yuya here that a config option is better here.
@yuja @pulkit Definitely, adding a config option is better than any other alternative. Can I start to work on this? (I mean adding config option which will work for all history editing commands)
Sounds good to me, though I don't have any good name for the config option.
To be clear, I think hg strip should be out of the control of the config
option to be added, because the strip in that case isn't a side effect of
history editing.
Queued, thanks.
@@ -829,6 +833,8 @@
userrevs = list(repo.revs(opts.get('auto_orphans'))) opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] opts['dest'] = '_destautoorphanrebase(SRC)'+ backup = ui.configbool('ui', 'history-editing-backup')
+ opts['backup'] = backup
This seems getting ugly. Maybe the option can be carried by rbsrt instead?
self.backupf = ui.configbool('ui', 'history-editing-backup')
In D3887#62825, @yuja wrote:Queued, thanks.
@@ -829,6 +833,8 @@
userrevs = list(repo.revs(opts.get('auto_orphans'))) opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] opts['dest'] = '_destautoorphanrebase(SRC)'+ backup = ui.configbool('ui', 'history-editing-backup')
+ opts['backup'] = backupThis seems getting ugly. Maybe the option can be carried by rbsrt instead?
self.backupf = ui.configbool('ui', 'history-editing-backup')
I like Yuya's suggestion here. @khanchi97 can you please follow-up?
In D3887#62850, @pulkit wrote:In D3887#62825, @yuja wrote:Queued, thanks.
@@ -829,6 +833,8 @@
userrevs = list(repo.revs(opts.get('auto_orphans'))) opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] opts['dest'] = '_destautoorphanrebase(SRC)'+ backup = ui.configbool('ui', 'history-editing-backup')
+ opts['backup'] = backupThis seems getting ugly. Maybe the option can be carried by rbsrt instead?
self.backupf = ui.configbool('ui', 'history-editing-backup')I like Yuya's suggestion here. @khanchi97 can you please follow-up?
Okay, I will send a follow-up.
Path | Packages | |||
---|---|---|---|---|
M | hgext/rebase.py (25 lines) | |||
M | mercurial/repair.py (10 lines) | |||
M | mercurial/scmutil.py (5 lines) | |||
A | M | tests/test-rebase-backup.t (150 lines) |
(desc, repo[self.state[rev]])) | (desc, repo[self.state[rev]])) | ||||
if not tr: | if not tr: | ||||
# When not using single transaction, store state after each | # When not using single transaction, store state after each | ||||
# commit is completely done. On InterventionRequired, we thus | # commit is completely done. On InterventionRequired, we thus | ||||
# won't store the status. Instead, we'll hit the "len(parents) == 2" | # won't store the status. Instead, we'll hit the "len(parents) == 2" | ||||
# case and realize that the commit was in progress. | # case and realize that the commit was in progress. | ||||
self.storestatus() | self.storestatus() | ||||
def _finishrebase(self): | def _finishrebase(self, backup=True): | ||||
""" | |||||
backup: if False, no backup will be stored when stripping rebased | |||||
revisions | |||||
""" | |||||
repo, ui, opts = self.repo, self.ui, self.opts | repo, ui, opts = self.repo, self.ui, self.opts | ||||
fm = ui.formatter('rebase', opts) | fm = ui.formatter('rebase', opts) | ||||
fm.startitem() | fm.startitem() | ||||
if self.collapsef: | if self.collapsef: | ||||
p1, p2, _base = defineparents(repo, min(self.state), self.destmap, | p1, p2, _base = defineparents(repo, min(self.state), self.destmap, | ||||
self.state, self.skipped, | self.state, self.skipped, | ||||
self.obsoletenotrebased) | self.obsoletenotrebased) | ||||
editopt = opts.get('edit') | editopt = opts.get('edit') | ||||
if newwd not in [c.rev() for c in repo[None].parents()]: | if newwd not in [c.rev() for c in repo[None].parents()]: | ||||
ui.note(_("update back to initial working directory parent\n")) | ui.note(_("update back to initial working directory parent\n")) | ||||
hg.updaterepo(repo, newwd, overwrite=False) | hg.updaterepo(repo, newwd, overwrite=False) | ||||
collapsedas = None | collapsedas = None | ||||
if self.collapsef and not self.keepf: | if self.collapsef and not self.keepf: | ||||
collapsedas = newnode | collapsedas = newnode | ||||
clearrebased(ui, repo, self.destmap, self.state, self.skipped, | clearrebased(ui, repo, self.destmap, self.state, self.skipped, | ||||
collapsedas, self.keepf, fm=fm) | collapsedas, self.keepf, fm=fm, backup=backup) | ||||
clearstatus(repo) | clearstatus(repo) | ||||
clearcollapsemsg(repo) | clearcollapsemsg(repo) | ||||
ui.note(_("rebase completed\n")) | ui.note(_("rebase completed\n")) | ||||
util.unlinkpath(repo.sjoin('undo'), ignoremissing=True) | util.unlinkpath(repo.sjoin('undo'), ignoremissing=True) | ||||
if self.skipped: | if self.skipped: | ||||
skippedlen = len(self.skipped) | skippedlen = len(self.skipped) | ||||
if opts.get('auto_orphans'): | if opts.get('auto_orphans'): | ||||
for key in opts: | for key in opts: | ||||
if key != 'auto_orphans' and opts.get(key): | if key != 'auto_orphans' and opts.get(key): | ||||
raise error.Abort(_('--auto-orphans is incompatible with %s') % | raise error.Abort(_('--auto-orphans is incompatible with %s') % | ||||
('--' + key)) | ('--' + key)) | ||||
userrevs = list(repo.revs(opts.get('auto_orphans'))) | userrevs = list(repo.revs(opts.get('auto_orphans'))) | ||||
opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] | opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] | ||||
opts['dest'] = '_destautoorphanrebase(SRC)' | opts['dest'] = '_destautoorphanrebase(SRC)' | ||||
backup = ui.configbool('ui', 'history-editing-backup') | |||||
opts['backup'] = backup | |||||
if dryrun: | if dryrun: | ||||
return _dryrunrebase(ui, repo, opts) | return _dryrunrebase(ui, repo, opts) | ||||
elif inmemory: | elif 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. | ||||
overrides = {('rebase', 'singletransaction'): True} | overrides = {('rebase', 'singletransaction'): True} | ||||
with ui.configoverride(overrides, 'rebase'): | with ui.configoverride(overrides, 'rebase'): | ||||
return _dorebase(ui, repo, opts, inmemory=inmemory) | return _dorebase(ui, repo, opts, inmemory=inmemory) | ||||
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')) | ||||
_dorebase(ui, repo, {'abort': True}) | _dorebase(ui, repo, {'abort': True}) | ||||
return _dorebase(ui, repo, opts, inmemory=False) | return _dorebase(ui, repo, opts, inmemory=False) | ||||
else: | else: | ||||
return _dorebase(ui, repo, opts) | return _dorebase(ui, repo, opts) | ||||
def _dryrunrebase(ui, repo, opts): | def _dryrunrebase(ui, repo, opts): | ||||
rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) | rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) | ||||
confirm = opts.get('confirm') | confirm = opts.get('confirm') | ||||
backup = opts.get('backup') | |||||
if confirm: | if confirm: | ||||
ui.status(_('starting in-memory rebase\n')) | ui.status(_('starting in-memory rebase\n')) | ||||
else: | else: | ||||
ui.status(_('starting dry-run rebase; repository will not be ' | ui.status(_('starting dry-run rebase; repository will not be ' | ||||
'changed\n')) | 'changed\n')) | ||||
with repo.wlock(), repo.lock(): | with repo.wlock(), repo.lock(): | ||||
needsabort = True | needsabort = True | ||||
try: | try: | ||||
overrides = {('rebase', 'singletransaction'): True} | overrides = {('rebase', 'singletransaction'): True} | ||||
with ui.configoverride(overrides, 'rebase'): | with ui.configoverride(overrides, 'rebase'): | ||||
_origrebase(ui, repo, opts, rbsrt, inmemory=True, | _origrebase(ui, repo, opts, rbsrt, inmemory=True, | ||||
leaveunfinished=True) | leaveunfinished=True) | ||||
except error.InMemoryMergeConflictsError: | except error.InMemoryMergeConflictsError: | ||||
ui.status(_('hit a merge conflict\n')) | ui.status(_('hit a merge conflict\n')) | ||||
return 1 | return 1 | ||||
else: | else: | ||||
if confirm: | if confirm: | ||||
ui.status(_('rebase completed successfully\n')) | ui.status(_('rebase completed successfully\n')) | ||||
if not ui.promptchoice(_(b'apply changes (yn)?' | if not ui.promptchoice(_(b'apply changes (yn)?' | ||||
b'$$ &Yes $$ &No')): | b'$$ &Yes $$ &No')): | ||||
# finish unfinished rebase | # finish unfinished rebase | ||||
rbsrt._finishrebase() | rbsrt._finishrebase(backup=backup) | ||||
else: | else: | ||||
rbsrt._prepareabortorcontinue(isabort=True, backup=False, | rbsrt._prepareabortorcontinue(isabort=True, backup=False, | ||||
suppwarns=True) | suppwarns=True) | ||||
needsabort = False | needsabort = False | ||||
else: | else: | ||||
ui.status(_('dry-run rebase completed successfully; run without' | ui.status(_('dry-run rebase completed successfully; run without' | ||||
' -n/--dry-run to perform this rebase\n')) | ' -n/--dry-run to perform this rebase\n')) | ||||
return 0 | return 0 | ||||
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 | ||||
# used in the 'hg pull --rebase' case, see issue 5214. | # used in the 'hg pull --rebase' case, see issue 5214. | ||||
destspace = opts.get('_destspace') | destspace = opts.get('_destspace') | ||||
contf = opts.get('continue') | contf = opts.get('continue') | ||||
abortf = opts.get('abort') | abortf = opts.get('abort') | ||||
backup = opts.get('backup') | |||||
if opts.get('interactive'): | if opts.get('interactive'): | ||||
try: | try: | ||||
if extensions.find('histedit'): | if extensions.find('histedit'): | ||||
enablehistedit = '' | enablehistedit = '' | ||||
except KeyError: | except KeyError: | ||||
enablehistedit = " --config extensions.histedit=" | enablehistedit = " --config extensions.histedit=" | ||||
help = "hg%s help -e histedit" % enablehistedit | help = "hg%s help -e histedit" % enablehistedit | ||||
msg = _("interactive history editing is supported by the " | msg = _("interactive history editing is supported by the " | ||||
raise error.Abort( | raise error.Abort( | ||||
_('abort and continue do not allow specifying revisions')) | _('abort and continue do not allow specifying revisions')) | ||||
if abortf and opts.get('tool', False): | if abortf and opts.get('tool', False): | ||||
ui.warn(_('tool option will be ignored\n')) | ui.warn(_('tool option will be ignored\n')) | ||||
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, backup=backup) | ||||
if retcode is not None: | if retcode is not None: | ||||
return retcode | return retcode | ||||
else: | else: | ||||
destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef, | destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef, | ||||
revf, destspace=destspace) | revf, destspace=destspace) | ||||
retcode = rbsrt._preparenewrebase(destmap) | retcode = rbsrt._preparenewrebase(destmap) | ||||
if retcode is not None: | if retcode is not None: | ||||
return retcode | return retcode | ||||
# Same logic for the dirstate guard, except we don't create one when | # Same logic for the dirstate guard, except we don't create one when | ||||
# rebasing in-memory (it's not needed). | # rebasing in-memory (it's not needed). | ||||
dsguard = None | dsguard = None | ||||
if singletr and not inmemory: | if singletr and not inmemory: | ||||
dsguard = dirstateguard.dirstateguard(repo, 'rebase') | dsguard = dirstateguard.dirstateguard(repo, 'rebase') | ||||
with util.acceptintervention(dsguard): | with util.acceptintervention(dsguard): | ||||
rbsrt._performrebase(tr) | rbsrt._performrebase(tr) | ||||
if not leaveunfinished: | if not leaveunfinished: | ||||
rbsrt._finishrebase() | rbsrt._finishrebase(backup=backup) | ||||
def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None, | def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None, | ||||
revf=None, destspace=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 | ||||
for rev in sorted(state): | for rev in sorted(state): | ||||
parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] | parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] | ||||
# if all parents of this revision are done, then so is this revision | # if all parents of this revision are done, then so is this revision | ||||
if parents and all((state.get(p) == p for p in parents)): | if parents and all((state.get(p) == p for p in parents)): | ||||
state[rev] = rev | state[rev] = rev | ||||
return originalwd, destmap, state | return originalwd, destmap, state | ||||
def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None, | def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None, | ||||
keepf=False, fm=None): | keepf=False, fm=None, backup=True): | ||||
"""dispose of rebased revision at the end of the rebase | """dispose of rebased revision at the end of the rebase | ||||
If `collapsedas` is not None, the rebase was a collapse whose result if the | If `collapsedas` is not None, the rebase was a collapse whose result if the | ||||
`collapsedas` node. | `collapsedas` node. | ||||
If `keepf` is not True, the rebase has --keep set and no nodes should be | If `keepf` is not True, the rebase has --keep set and no nodes should be | ||||
removed (but bookmarks still need to be moved). | removed (but bookmarks still need to be moved). | ||||
If `backup` is False, no backup will be stored when stripping rebased | |||||
revisions. | |||||
""" | """ | ||||
tonode = repo.changelog.node | tonode = repo.changelog.node | ||||
replacements = {} | replacements = {} | ||||
moves = {} | moves = {} | ||||
for rev, newrev in sorted(state.items()): | for rev, newrev in sorted(state.items()): | ||||
if newrev >= 0 and newrev != rev: | if newrev >= 0 and newrev != rev: | ||||
oldnode = tonode(rev) | oldnode = tonode(rev) | ||||
newnode = collapsedas or tonode(newrev) | newnode = collapsedas or tonode(newrev) | ||||
moves[oldnode] = newnode | moves[oldnode] = newnode | ||||
if not keepf: | if not keepf: | ||||
if rev in skipped: | if rev in skipped: | ||||
succs = () | succs = () | ||||
else: | else: | ||||
succs = (newnode,) | succs = (newnode,) | ||||
replacements[oldnode] = succs | replacements[oldnode] = succs | ||||
scmutil.cleanupnodes(repo, replacements, 'rebase', moves) | scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup) | ||||
if fm: | if fm: | ||||
hf = fm.hexfunc | hf = fm.hexfunc | ||||
fl = fm.formatlist | fl = fm.formatlist | ||||
fd = fm.formatdict | fd = fm.formatdict | ||||
nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node') | nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node') | ||||
for oldn, newn in replacements.iteritems()}, | for oldn, newn in replacements.iteritems()}, | ||||
key="oldnode", value="newnodes") | key="oldnode", value="newnodes") | ||||
fm.data(nodechanges=nodechanges) | fm.data(nodechanges=nodechanges) |
def addnodes(self, nodes): | def addnodes(self, nodes): | ||||
self.nodelist.extend(nodes) | self.nodelist.extend(nodes) | ||||
def __call__(self, tr): | def __call__(self, tr): | ||||
roots = safestriproots(self.ui, self.repo, self.nodelist) | roots = safestriproots(self.ui, self.repo, self.nodelist) | ||||
if roots: | if roots: | ||||
strip(self.ui, self.repo, roots, self.backup, self.topic) | strip(self.ui, self.repo, roots, self.backup, self.topic) | ||||
def delayedstrip(ui, repo, nodelist, topic=None): | def delayedstrip(ui, repo, nodelist, topic=None, backup=True): | ||||
"""like strip, but works inside transaction and won't strip irreverent revs | """like strip, but works inside transaction and won't strip irreverent revs | ||||
nodelist must explicitly contain all descendants. Otherwise a warning will | nodelist must explicitly contain all descendants. Otherwise a warning will | ||||
be printed that some nodes are not stripped. | be printed that some nodes are not stripped. | ||||
Always do a backup. The last non-None "topic" will be used as the backup | Will do a backup if `backup` is True. The last non-None "topic" will be | ||||
topic name. The default backup topic name is "backup". | used as the backup topic name. The default backup topic name is "backup". | ||||
""" | """ | ||||
tr = repo.currenttransaction() | tr = repo.currenttransaction() | ||||
if not tr: | if not tr: | ||||
nodes = safestriproots(ui, repo, nodelist) | nodes = safestriproots(ui, repo, nodelist) | ||||
return strip(ui, repo, nodes, True, topic) | return strip(ui, repo, nodes, backup=backup, topic=topic) | ||||
# transaction postclose callbacks are called in alphabet order. | # transaction postclose callbacks are called in alphabet order. | ||||
# use '\xff' as prefix so we are likely to be called last. | # use '\xff' as prefix so we are likely to be called last. | ||||
callback = tr.getpostclose('\xffstrip') | callback = tr.getpostclose('\xffstrip') | ||||
if callback is None: | if callback is None: | ||||
callback = stripcallback(ui, repo, True, topic) | callback = stripcallback(ui, repo, backup=backup, topic=topic) | ||||
tr.addpostclose('\xffstrip', callback) | tr.addpostclose('\xffstrip', callback) | ||||
if topic: | if topic: | ||||
callback.topic = topic | callback.topic = topic | ||||
callback.addnodes(nodelist) | callback.addnodes(nodelist) | ||||
def stripmanifest(repo, striprev, tr, files): | def stripmanifest(repo, striprev, tr, files): | ||||
revlog = repo.manifestlog._revlog | revlog = repo.manifestlog._revlog | ||||
revlog.strip(striprev, tr) | revlog.strip(striprev, tr) |
def __init__(self, repo, revcontainer): | def __init__(self, repo, revcontainer): | ||||
self._torev = repo.changelog.rev | self._torev = repo.changelog.rev | ||||
self._revcontains = revcontainer.__contains__ | self._revcontains = revcontainer.__contains__ | ||||
def __contains__(self, node): | def __contains__(self, node): | ||||
return self._revcontains(self._torev(node)) | return self._revcontains(self._torev(node)) | ||||
def cleanupnodes(repo, replacements, operation, moves=None, metadata=None, | def cleanupnodes(repo, replacements, operation, moves=None, metadata=None, | ||||
fixphase=False, targetphase=None): | fixphase=False, targetphase=None, backup=True): | ||||
"""do common cleanups when old nodes are replaced by new nodes | """do common cleanups when old nodes are replaced by new nodes | ||||
That includes writing obsmarkers or stripping nodes, and moving bookmarks. | That includes writing obsmarkers or stripping nodes, and moving bookmarks. | ||||
(we might also want to move working directory parent in the future) | (we might also want to move working directory parent in the future) | ||||
By default, bookmark moves are calculated automatically from 'replacements', | By default, bookmark moves are calculated automatically from 'replacements', | ||||
but 'moves' can be used to override that. Also, 'moves' may include | but 'moves' can be used to override that. Also, 'moves' may include | ||||
additional bookmark moves that should not have associated obsmarkers. | additional bookmark moves that should not have associated obsmarkers. | ||||
if s or not isobs(n)] | if s or not isobs(n)] | ||||
if rels: | if rels: | ||||
obsolete.createmarkers(repo, rels, operation=operation, | obsolete.createmarkers(repo, rels, operation=operation, | ||||
metadata=metadata) | metadata=metadata) | ||||
else: | else: | ||||
from . import repair # avoid import cycle | from . import repair # avoid import cycle | ||||
tostrip = list(replacements) | tostrip = list(replacements) | ||||
if tostrip: | if tostrip: | ||||
repair.delayedstrip(repo.ui, repo, tostrip, operation) | repair.delayedstrip(repo.ui, repo, tostrip, operation, | ||||
backup=backup) | |||||
def addremove(repo, matcher, prefix, opts=None): | def addremove(repo, matcher, prefix, opts=None): | ||||
if opts is None: | if opts is None: | ||||
opts = {} | opts = {} | ||||
m = matcher | m = matcher | ||||
dry_run = opts.get('dry_run') | dry_run = opts.get('dry_run') | ||||
try: | try: | ||||
similarity = float(opts.get('similarity') or 0) | similarity = float(opts.get('similarity') or 0) |
$ cat << EOF >> $HGRCPATH | |||||
> [extensions] | |||||
> rebase= | |||||
> EOF | |||||
========================================== | |||||
Test history-editing-backup config option | | |||||
========================================== | |||||
Test with Pre-obsmarker rebase: | |||||
1) When config option is not set: | |||||
$ hg init repo1 | |||||
$ cd repo1 | |||||
$ echo a>a | |||||
$ hg ci -qAma | |||||
$ echo b>b | |||||
$ hg ci -qAmb | |||||
$ echo c>c | |||||
$ hg ci -qAmc | |||||
$ hg up 0 -q | |||||
$ echo d>d | |||||
$ hg ci -qAmd | |||||
$ echo e>e | |||||
$ hg ci -qAme | |||||
$ hg log -GT "{rev}: {firstline(desc)}\n" | |||||
@ 4: e | |||||
| | |||||
o 3: d | |||||
| | |||||
| o 2: c | |||||
| | | |||||
| o 1: b | |||||
|/ | |||||
o 0: a | |||||
$ hg rebase -s 1 -d . | |||||
rebasing 1:d2ae7f538514 "b" | |||||
rebasing 2:177f92b77385 "c" | |||||
saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/d2ae7f538514-c7ed7a78-rebase.hg | |||||
$ hg log -GT "{rev}: {firstline(desc)}\n" | |||||
o 4: c | |||||
| | |||||
o 3: b | |||||
| | |||||
@ 2: e | |||||
| | |||||
o 1: d | |||||
| | |||||
o 0: a | |||||
2) When config option is set: | |||||
$ cat << EOF >> $HGRCPATH | |||||
> [ui] | |||||
> history-editing-backup = False | |||||
> EOF | |||||
$ echo f>f | |||||
$ hg ci -Aqmf | |||||
$ echo g>g | |||||
$ hg ci -Aqmg | |||||
$ hg log -GT "{rev}: {firstline(desc)}\n" | |||||
@ 6: g | |||||
| | |||||
o 5: f | |||||
| | |||||
| o 4: c | |||||
| | | |||||
| o 3: b | |||||
|/ | |||||
o 2: e | |||||
| | |||||
o 1: d | |||||
| | |||||
o 0: a | |||||
$ hg rebase -s 3 -d . | |||||
rebasing 3:05bff2a95b12 "b" | |||||
rebasing 4:1762bde4404d "c" | |||||
$ hg log -GT "{rev}: {firstline(desc)}\n" | |||||
o 6: c | |||||
| | |||||
o 5: b | |||||
| | |||||
@ 4: g | |||||
| | |||||
o 3: f | |||||
| | |||||
o 2: e | |||||
| | |||||
o 1: d | |||||
| | |||||
o 0: a | |||||
Test when rebased revisions are stripped during abort: | |||||
====================================================== | |||||
$ echo conflict > c | |||||
$ hg ci -Am "conflict with c" | |||||
adding c | |||||
created new head | |||||
$ hg log -GT "{rev}: {firstline(desc)}\n" | |||||
@ 7: conflict with c | |||||
| | |||||
| o 6: c | |||||
| | | |||||
| o 5: b | |||||
|/ | |||||
o 4: g | |||||
| | |||||
o 3: f | |||||
| | |||||
o 2: e | |||||
| | |||||
o 1: d | |||||
| | |||||
o 0: a | |||||
When history-editing-backup = True: | |||||
$ cat << EOF >> $HGRCPATH | |||||
> [ui] | |||||
> history-editing-backup = True | |||||
> EOF | |||||
$ hg rebase -s 5 -d . | |||||
rebasing 5:1f8148a544ee "b" | |||||
rebasing 6:f8bc7d28e573 "c" | |||||
merging c | |||||
warning: conflicts while merging c! (edit, then use 'hg resolve --mark') | |||||
unresolved conflicts (see hg resolve, then hg rebase --continue) | |||||
[1] | |||||
$ hg rebase --abort | |||||
saved backup bundle to $TESTTMP/repo1/.hg/strip-backup/818c1a43c916-2b644d96-backup.hg | |||||
rebase aborted | |||||
When history-editing-backup = False: | |||||
$ cat << EOF >> $HGRCPATH | |||||
> [ui] | |||||
> history-editing-backup = False | |||||
> EOF | |||||
$ hg rebase -s 5 -d . | |||||
rebasing 5:1f8148a544ee "b" | |||||
rebasing 6:f8bc7d28e573 "c" | |||||
merging c | |||||
warning: conflicts while merging c! (edit, then use 'hg resolve --mark') | |||||
unresolved conflicts (see hg resolve, then hg rebase --continue) | |||||
[1] | |||||
$ hg rebase --abort | |||||
rebase aborted | |||||
$ cd .. | |||||