Patchwork D3959: rebase: add --stop option to stop rebase at any point (issue5206)

login
register
mail settings
Submitter phabricator
Date July 24, 2018, 6:04 p.m.
Message ID <692416632d2e4a1280c85908b7bbf69a@localhost.localdomain>
Download mbox | patch
Permalink /patch/32932/
State Not Applicable
Headers show

Comments

phabricator - July 24, 2018, 6:04 p.m.
khanchi97 updated this revision to Diff 9654.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D3959?vs=9642&id=9654

REVISION DETAIL
  https://phab.mercurial-scm.org/D3959

AFFECTED FILES
  hgext/rebase.py
  tests/test-rebase-obsolete.t

CHANGE DETAILS




To: khanchi97, #hg-reviewers
Cc: pulkit, yuja, mercurial-devel
Yuya Nishihara - Aug. 8, 2018, 1:06 p.m.
> -    def _finishrebase(self):
> +    def _finishrebase(self, stoprebase=False):
>          repo, ui, opts = self.repo, self.ui, self.opts
>          fm = ui.formatter('rebase', opts)
>          fm.startitem()
> +        if stoprebase:
> +            self.restorestatus()
> +            if self.collapsef:
> +                ui.status(_("cannot stop in --collapse session\n"))
> +                return

raise Abort because it's an error?

> +
> +            allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
> +            if not (self.keepf or allowunstable):
> +                raise error.Abort(_("can't remove original changesets with"
> +                      " unrebased descendants"),
> +                    hint=_('either enable obsmarkers to allow unstable '
> +                           'revisions or use --keep to keep original '
> +                           'changesets'))

It will be nice if we can move the pre-process out from `_finishrebase()`,
and get rid of the `stoprebase` flag somehow.

>          if self.collapsef:
>              p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
>                                            self.state, self.skipped,
> @@ -626,7 +640,10 @@
>              newwd = self.originalwd
>          if newwd not in [c.rev() for c in repo[None].parents()]:
>              ui.note(_("update back to initial working directory parent\n"))
> -            hg.updaterepo(repo, newwd, overwrite=False)
> +            if stoprebase:
> +                hg.updaterepo(repo, newwd, overwrite=True)
> +            else:
> +                hg.updaterepo(repo, newwd, overwrite=False)

This implies an interrupted merge won't be cleared if no update is required.
(try `hg up 3 && hg rebase -s 1 -d 5` in the first test you've added.)

Perhaps the `rebase --stop` flow can be processed as follows?

```
       rbsrt = rebaseruntime(repo, ui)
       rbsrt.restorestatus()
       ... check unsupported options ...
       with repo.wlock(), repo.lock():
           if needupdate(repo, state):
               .. update to the current working revision with overwrite=True
               .. to clear interrupted merge
           rbsrt._finishrebase()
```
phabricator - Aug. 8, 2018, 1:06 p.m.
yuja added a comment.


  > - def _finishrebase(self): +    def _finishrebase(self, stoprebase=False): repo, ui, opts = self.repo, self.ui, self.opts fm = ui.formatter('rebase', opts) fm.startitem() +        if stoprebase: +            self.restorestatus() +            if self.collapsef: +                ui.status(_("cannot stop in --collapse session\n")) +                return
  
  raise Abort because it's an error?
  
  > +
  >  +            allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
  >  +            if not (self.keepf or allowunstable):
  >  +                raise error.Abort(_("can't remove original changesets with"
  >  +                      " unrebased descendants"),
  >  +                    hint=_('either enable obsmarkers to allow unstable '
  >  +                           'revisions or use --keep to keep original '
  >  +                           'changesets'))
  
  It will be nice if we can move the pre-process out from `_finishrebase()`,
  and get rid of the `stoprebase` flag somehow.
  
  >   if self.collapsef:
  >       p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
  >                                     self.state, self.skipped,
  > 
  > @@ -626,7 +640,10 @@
  > 
  >       newwd = self.originalwd
  >   if newwd not in [c.rev() for c in repo[None].parents()]:
  >       ui.note(_("update back to initial working directory parent\n"))
  > 
  > - hg.updaterepo(repo, newwd, overwrite=False) +            if stoprebase: +                hg.updaterepo(repo, newwd, overwrite=True) +            else: +                hg.updaterepo(repo, newwd, overwrite=False)
  
  This implies an interrupted merge won't be cleared if no update is required.
  (try `hg up 3 && hg rebase -s 1 -d 5` in the first test you've added.)
  
  Perhaps the `rebase --stop` flow can be processed as follows?
  
    rbsrt = rebaseruntime(repo, ui)
    rbsrt.restorestatus()
    ... check unsupported options ...
    with repo.wlock(), repo.lock():
        if needupdate(repo, state):
            .. update to the current working revision with overwrite=True
            .. to clear interrupted merge
        rbsrt._finishrebase()

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D3959

To: khanchi97, #hg-reviewers
Cc: pulkit, yuja, mercurial-devel

Patch

diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t
--- a/tests/test-rebase-obsolete.t
+++ b/tests/test-rebase-obsolete.t
@@ -15,6 +15,7 @@ 
   > [extensions]
   > rebase=
   > drawdag=$TESTDIR/drawdag.py
+  > strip=
   > EOF
 
 Setup rebase canonical repo
@@ -1788,3 +1789,216 @@ 
   |
   o  0:426bada5c675 A
   
+====================
+Test --stop option |
+====================
+  $ cd ..
+  $ hg init rbstop
+  $ cd rbstop
+  $ echo a>a
+  $ hg ci -Aqma
+  $ echo b>b
+  $ hg ci -Aqmb
+  $ echo c>c
+  $ hg ci -Aqmc
+  $ echo d>d
+  $ hg ci -Aqmd
+  $ hg up 0 -q
+  $ echo f>f
+  $ hg ci -Aqmf
+  $ echo D>d
+  $ hg ci -Aqm "conflict with d"
+  $ hg up 0 -q
+  $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
+  o  5:00bfc9898aeb test
+  |  conflict with d
+  |
+  o  4:dafd40200f93 test
+  |  f
+  |
+  | o  3:055a42cdd887 test
+  | |  d
+  | |
+  | o  2:177f92b77385 test
+  | |  c
+  | |
+  | o  1:d2ae7f538514 test
+  |/   b
+  |
+  @  0:cb9a9f314b8b test
+     a
+  
+  $ hg rebase -s 1 -d 5
+  rebasing 1:d2ae7f538514 "b"
+  rebasing 2:177f92b77385 "c"
+  rebasing 3:055a42cdd887 "d"
+  merging d
+  warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+  unresolved conflicts (see hg resolve, then hg rebase --continue)
+  [1]
+  $ hg rebase --stop
+  1 new orphan changesets
+  $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
+  o  7:7fffad344617 test
+  |  c
+  |
+  o  6:b15528633407 test
+  |  b
+  |
+  o  5:00bfc9898aeb test
+  |  conflict with d
+  |
+  o  4:dafd40200f93 test
+  |  f
+  |
+  | *  3:055a42cdd887 test
+  | |  d
+  | |
+  | x  2:177f92b77385 test
+  | |  c
+  | |
+  | x  1:d2ae7f538514 test
+  |/   b
+  |
+  @  0:cb9a9f314b8b test
+     a
+  
+Test it aborts if unstable csets is not allowed:
+===============================================
+  $ cat >> $HGRCPATH << EOF
+  > [experimental]
+  > evolution.allowunstable=False
+  > EOF
+
+  $ hg strip 6 --no-backup -q
+  $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
+  o  5:00bfc9898aeb test
+  |  conflict with d
+  |
+  o  4:dafd40200f93 test
+  |  f
+  |
+  | o  3:055a42cdd887 test
+  | |  d
+  | |
+  | o  2:177f92b77385 test
+  | |  c
+  | |
+  | o  1:d2ae7f538514 test
+  |/   b
+  |
+  @  0:cb9a9f314b8b test
+     a
+  
+  $ hg rebase -s 1 -d 5
+  rebasing 1:d2ae7f538514 "b"
+  rebasing 2:177f92b77385 "c"
+  rebasing 3:055a42cdd887 "d"
+  merging d
+  warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+  unresolved conflicts (see hg resolve, then hg rebase --continue)
+  [1]
+  $ hg rebase --stop
+  abort: can't remove original changesets with unrebased descendants
+  (either enable obsmarkers to allow unstable revisions or use --keep to keep original changesets)
+  [255]
+  $ hg rebase --abort
+  saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
+  rebase aborted
+
+Test --stop when --keep is passed:
+==================================
+  $ hg rebase -s 1 -d 5 --keep
+  rebasing 1:d2ae7f538514 "b"
+  rebasing 2:177f92b77385 "c"
+  rebasing 3:055a42cdd887 "d"
+  merging d
+  warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+  unresolved conflicts (see hg resolve, then hg rebase --continue)
+  [1]
+  $ hg rebase --stop
+  $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
+  o  7:7fffad344617 test
+  |  c
+  |
+  o  6:b15528633407 test
+  |  b
+  |
+  o  5:00bfc9898aeb test
+  |  conflict with d
+  |
+  o  4:dafd40200f93 test
+  |  f
+  |
+  | o  3:055a42cdd887 test
+  | |  d
+  | |
+  | o  2:177f92b77385 test
+  | |  c
+  | |
+  | o  1:d2ae7f538514 test
+  |/   b
+  |
+  @  0:cb9a9f314b8b test
+     a
+  
+Test --stop aborts when --collapse was passed:
+=============================================
+  $ cat >> $HGRCPATH << EOF
+  > [experimental]
+  > evolution.allowunstable=True
+  > EOF
+
+  $ hg strip 6
+  saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
+  $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
+  o  5:00bfc9898aeb test
+  |  conflict with d
+  |
+  o  4:dafd40200f93 test
+  |  f
+  |
+  | o  3:055a42cdd887 test
+  | |  d
+  | |
+  | o  2:177f92b77385 test
+  | |  c
+  | |
+  | o  1:d2ae7f538514 test
+  |/   b
+  |
+  @  0:cb9a9f314b8b test
+     a
+  
+  $ hg rebase -s 1 -d 5 --collapse -m "collapsed b c d"
+  rebasing 1:d2ae7f538514 "b"
+  rebasing 2:177f92b77385 "c"
+  rebasing 3:055a42cdd887 "d"
+  merging d
+  warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+  unresolved conflicts (see hg resolve, then hg rebase --continue)
+  [1]
+  $ hg rebase --stop
+  cannot stop in --collapse session
+  $ hg rebase --abort
+  rebase aborted
+  $ hg diff
+  $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
+  o  5:00bfc9898aeb test
+  |  conflict with d
+  |
+  o  4:dafd40200f93 test
+  |  f
+  |
+  | o  3:055a42cdd887 test
+  | |  d
+  | |
+  | o  2:177f92b77385 test
+  | |  c
+  | |
+  | o  1:d2ae7f538514 test
+  |/   b
+  |
+  @  0:cb9a9f314b8b test
+     a
+  
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -585,10 +585,24 @@ 
             # case and realize that the commit was in progress.
             self.storestatus()
 
-    def _finishrebase(self):
+    def _finishrebase(self, stoprebase=False):
         repo, ui, opts = self.repo, self.ui, self.opts
         fm = ui.formatter('rebase', opts)
         fm.startitem()
+        if stoprebase:
+            self.restorestatus()
+            if self.collapsef:
+                ui.status(_("cannot stop in --collapse session\n"))
+                return
+
+            allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
+            if not (self.keepf or allowunstable):
+                raise error.Abort(_("can't remove original changesets with"
+                      " unrebased descendants"),
+                    hint=_('either enable obsmarkers to allow unstable '
+                           'revisions or use --keep to keep original '
+                           'changesets'))
+
         if self.collapsef:
             p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
                                           self.state, self.skipped,
@@ -626,7 +640,10 @@ 
             newwd = self.originalwd
         if newwd not in [c.rev() for c in repo[None].parents()]:
             ui.note(_("update back to initial working directory parent\n"))
-            hg.updaterepo(repo, newwd, overwrite=False)
+            if stoprebase:
+                hg.updaterepo(repo, newwd, overwrite=True)
+            else:
+                hg.updaterepo(repo, newwd, overwrite=False)
 
         collapsedas = None
         if self.collapsef and not self.keepf:
@@ -670,6 +687,7 @@ 
     ('D', 'detach', False, _('(DEPRECATED)')),
     ('i', 'interactive', False, _('(DEPRECATED)')),
     ('t', 'tool', '', _('specify merge tool')),
+    ('', 'stop', False, _('stop interrupted rebase')),
     ('c', 'continue', False, _('continue an interrupted rebase')),
     ('a', 'abort', False, _('abort an interrupted rebase')),
     ('', 'auto-orphans', '', _('automatically rebase orphan revisions '
@@ -800,6 +818,7 @@ 
     opts = pycompat.byteskwargs(opts)
     inmemory = ui.configbool('rebase', 'experimental.inmemory')
     dryrun = opts.get('dry_run')
+    stop = opts.get('stop')
     if dryrun:
         if opts.get('abort'):
             raise error.Abort(_('cannot specify both --dry-run and --abort'))
@@ -832,6 +851,12 @@ 
 
     if dryrun:
         return _dryrunrebase(ui, repo, opts)
+    elif stop:
+        #todo: raise error for conflicting options
+        rbsrt = rebaseruntime(repo, ui)
+        with repo.wlock(), repo.lock():
+            rbsrt._finishrebase(stoprebase=True)
+            return 0
     elif inmemory:
         try:
             # in-memory merge doesn't support conflicts, so if we hit any, abort