diff --git a/hgext/rebase.py b/hgext/rebase.py --- a/hgext/rebase.py +++ b/hgext/rebase.py @@ -585,7 +585,11 @@ # case and realize that the commit was in progress. 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 fm = ui.formatter('rebase', opts) fm.startitem() @@ -632,7 +636,7 @@ if self.collapsef and not self.keepf: collapsedas = newnode clearrebased(ui, repo, self.destmap, self.state, self.skipped, - collapsedas, self.keepf, fm=fm) + collapsedas, self.keepf, fm=fm, backup=backup) clearstatus(repo) clearcollapsemsg(repo) @@ -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 if dryrun: return _dryrunrebase(ui, repo, opts) @@ -850,6 +856,7 @@ def _dryrunrebase(ui, repo, opts): rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) confirm = opts.get('confirm') + backup = opts.get('backup') if confirm: ui.status(_('starting in-memory rebase\n')) else: @@ -871,7 +878,7 @@ if not ui.promptchoice(_(b'apply changes (yn)?' b'$$ &Yes $$ &No')): # finish unfinished rebase - rbsrt._finishrebase() + rbsrt._finishrebase(backup=backup) else: rbsrt._prepareabortorcontinue(isabort=True, backup=False, suppwarns=True) @@ -902,6 +909,7 @@ destspace = opts.get('_destspace') contf = opts.get('continue') abortf = opts.get('abort') + backup = opts.get('backup') if opts.get('interactive'): try: if extensions.find('histedit'): @@ -932,7 +940,7 @@ ms = mergemod.mergestate.read(repo) mergeutil.checkunresolved(ms) - retcode = rbsrt._prepareabortorcontinue(abortf) + retcode = rbsrt._prepareabortorcontinue(abortf, backup=backup) if retcode is not None: return retcode else: @@ -961,7 +969,7 @@ with util.acceptintervention(dsguard): rbsrt._performrebase(tr) if not leaveunfinished: - rbsrt._finishrebase() + rbsrt._finishrebase(backup=backup) def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None, revf=None, destspace=None): @@ -1728,7 +1736,7 @@ return originalwd, destmap, state 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 If `collapsedas` is not None, the rebase was a collapse whose result if the @@ -1736,6 +1744,9 @@ If `keepf` is not True, the rebase has --keep set and no nodes should be 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 replacements = {} @@ -1751,7 +1762,7 @@ else: succs = (newnode,) replacements[oldnode] = succs - scmutil.cleanupnodes(repo, replacements, 'rebase', moves) + scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup) if fm: hf = fm.hexfunc fl = fm.formatlist diff --git a/mercurial/repair.py b/mercurial/repair.py --- a/mercurial/repair.py +++ b/mercurial/repair.py @@ -298,24 +298,24 @@ if roots: 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 nodelist must explicitly contain all descendants. Otherwise a warning will be printed that some nodes are not stripped. - Always do a backup. The last non-None "topic" will be used as the backup - topic name. The default backup topic name is "backup". + Will do a backup if `backup` is True. The last non-None "topic" will be + used as the backup topic name. The default backup topic name is "backup". """ tr = repo.currenttransaction() if not tr: 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. # use '\xff' as prefix so we are likely to be called last. callback = tr.getpostclose('\xffstrip') if callback is None: - callback = stripcallback(ui, repo, True, topic) + callback = stripcallback(ui, repo, backup=backup, topic=topic) tr.addpostclose('\xffstrip', callback) if topic: callback.topic = topic diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -780,7 +780,7 @@ return self._revcontains(self._torev(node)) 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 That includes writing obsmarkers or stripping nodes, and moving bookmarks. @@ -905,7 +905,8 @@ from . import repair # avoid import cycle tostrip = list(replacements) 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): if opts is None: diff --git a/tests/test-rebase-backup.t b/tests/test-rebase-backup.t new file mode 100644 --- /dev/null +++ b/tests/test-rebase-backup.t @@ -0,0 +1,150 @@ + $ 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 .. +