This is an archive of the discontinued Mercurial Phabricator instance.

rebase: add --confirm option
ClosedPublic

Authored by khanchi97 on Jul 1 2018, 5:52 AM.

Details

Reviewers
None
Group Reviewers
hg-reviewers
Commits
rHG572dff5c946e: rebase: add --confirm option
Summary

This feature adds a functionality in rebase to confirm before applying
changes.
When there is no conflict and user confirm to apply actions, we just
finish the unfinished rebase. But when there is a conflict and user
confirm to apply actions then we can't just finish rebasing using
rbsrt._finishrebase() because in-memory merge doesn't support conflicts, so
we have to abort and run on-disk merge in this case.
And if user doesn't confirm to apply actions then simply abort the rebase.

Diff Detail

Repository
rHG Mercurial
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.

Event Timeline

khanchi97 created this revision.Jul 1 2018, 5:52 AM
yuja added a subscriber: yuja.Jul 9 2018, 9:15 AM

def _dryrunrebase(ui, repo, opts):

rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
  • ui.status(_('starting dry-run rebase; repository will not be changed\n'))

+ confirm = opts.get('confirm')
+ if confirm:
+ ui.status(_('starting rebase...\n'))
+ else:
+ ui.status(_('starting dry-run rebase; repository will not be '
+ 'changed\n'))

Nit: Perhaps --dry-run should precede --confirm, or "--dryrun --confirm" should
be rejected.

with repo.wlock(), repo.lock():
    try:
        overrides = {('rebase', 'singletransaction'): True}
        with ui.configoverride(overrides, 'rebase'):
            _origrebase(ui, repo, opts, rbsrt, inmemory=True,
                        leaveunfinished=True, supptrwarns=True)
    except error.InMemoryMergeConflictsError:

+ conflict = True

    ui.status(_('hit a merge conflict\n'))
    return 1
else:
  • ui.status(_('dry-run rebase completed successfully; run without '
  • '-n/--dry-run to perform this rebase\n'))

+ conflict = False
+ if confirm:
+ ui.status(_('rebase completed successfully\n'))
+ else:
+ ui.status(_('dry-run rebase completed successfully; run without'
+ ' -n/--dry-run to perform this rebase\n'))

    return 0
finally:
    # no need to store backup in case of dryrun
    rbsrt._prepareabortorcontinue(isabort=True, backup=False,
                                  suppwarns=True)

+ if confirm:
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):
+ if not conflict:
+ inmemory = ui.configbool('rebase',
+ 'experimental.inmemory')
+ return _dorebase(ui, repo, opts, inmemory=inmemory)
+ return _dorebase(ui, repo, opts)

Can't we just "finish" the last in-memory rebase if "apply changes" selected?

In D3870#60813, @yuja wrote:

def _dryrunrebase(ui, repo, opts):

rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
  • ui.status(_('starting dry-run rebase; repository will not be changed\n'))

+ confirm = opts.get('confirm')
+ if confirm:
+ ui.status(_('starting rebase...\n'))
+ else:
+ ui.status(_('starting dry-run rebase; repository will not be '
+ 'changed\n'))

Nit: Perhaps --dry-run should precede --confirm, or "--dryrun --confirm" should
be rejected.

I think later option is good.

with repo.wlock(), repo.lock():
    try:
        overrides = {('rebase', 'singletransaction'): True}
        with ui.configoverride(overrides, 'rebase'):
            _origrebase(ui, repo, opts, rbsrt, inmemory=True,
                        leaveunfinished=True, supptrwarns=True)
    except error.InMemoryMergeConflictsError:

+ conflict = True

    ui.status(_('hit a merge conflict\n'))
    return 1
else:
  • ui.status(_('dry-run rebase completed successfully; run without '
  • '-n/--dry-run to perform this rebase\n'))

+ conflict = False
+ if confirm:
+ ui.status(_('rebase completed successfully\n'))
+ else:
+ ui.status(_('dry-run rebase completed successfully; run without'
+ ' -n/--dry-run to perform this rebase\n'))

    return 0
finally:
    # no need to store backup in case of dryrun
    rbsrt._prepareabortorcontinue(isabort=True, backup=False,
                                  suppwarns=True)

+ if confirm:
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):
+ if not conflict:
+ inmemory = ui.configbool('rebase',
+ 'experimental.inmemory')
+ return _dorebase(ui, repo, opts, inmemory=inmemory)
+ return _dorebase(ui, repo, opts)

Can't we just "finish" the last in-memory rebase if "apply changes" selected?

Ah, I think we can :)
will update it, thanks!

khanchi97 edited the summary of this revision. (Show Details)Jul 9 2018, 11:43 PM
khanchi97 updated this revision to Diff 9486.
yuja added a comment.Jul 10 2018, 9:18 AM

This can't be applied, can you rebase?

  • a/hgext/rebase.py

+++ b/hgext/rebase.py
@@ -677,7 +677,7 @@

('a', 'abort', False, _('abort an interrupted rebase')),
('', 'auto-orphans', '', _('automatically rebase orphan revisions '
                           'in the specified revset (EXPERIMENTAL)')),
  • ] + cmdutil.dryrunopts + cmdutil.formatteropts,

+ ] + cmdutil.dryrunopts + cmdutil.formatteropts + cmdutil.confirmopts,

_('[-s REV | -b REV] [-d REV] [OPTION]'))

def rebase(ui, repo, **opts):

"""move changeset (and descendants) to a different branch

@@ -808,6 +808,10 @@

    raise error.Abort(_('cannot specify both --dry-run and --abort'))
if opts.get('continue'):
    raise error.Abort(_('cannot specify both --dry-run and --continue'))

+ if opts.get('confirm'):
+ raise error.Abort(_('cannot specify both --dry-run and --confirm'))
+ if opts.get('confirm'):
+ dryrun = True

Nit: we'll have to reject "--confirm --abort" and "--confirm --continue" as
well.

with repo.wlock(), repo.lock():
    try:
        overrides = {('rebase', 'singletransaction'): True}
        with ui.configoverride(overrides, 'rebase'):
            _origrebase(ui, repo, opts, rbsrt, inmemory=True,
                        leaveunfinished=True, supptrwarns=True)
    except error.InMemoryMergeConflictsError:

+ conflict = True

    ui.status(_('hit a merge conflict\n'))
    return 1
else:
  • ui.status(_('dry-run rebase completed successfully; run without '
  • '-n/--dry-run to perform this rebase\n'))

+ conflict = False
+ if confirm:
+ ui.status(_('rebase completed successfully\n'))
+ else:
+ ui.status(_('dry-run rebase completed successfully; run without'
+ ' -n/--dry-run to perform this rebase\n'))

    return 0
finally:
  • # no need to store backup in case of dryrun
  • rbsrt._prepareabortorcontinue(isabort=True, backup=False,
  • suppwarns=True)

+ if confirm and not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):
+ if conflict:
+ rbsrt._prepareabortorcontinue(isabort=True,
+ backup=False,
+ suppwarns=True)
+ _dorebase(ui, repo, opts, inmemory=False)
+ else:
+ # finish unfinished rebase
+ rbsrt._finishrebase()

I'm not a fan of doing much things "finally:" clause. Can you move this to
the "else:" clause? The "conflict" flag can be flipped to be a "needsabort",
and it has to be initialized before "try:".

khanchi97 updated this revision to Diff 9502.Jul 10 2018, 10:44 AM

@yuja Rebased, removed variable "needsabort/conflict" and moved that code to "except" and "else" clause accordingly.

yuja added a comment.Jul 11 2018, 7:16 AM

Still can't apply. Which revision is this patch based on?

with repo.wlock(), repo.lock():
    try:
        overrides = {('rebase', 'singletransaction'): True}
        with ui.configoverride(overrides, 'rebase'):
            _origrebase(ui, repo, opts, rbsrt, inmemory=True,
                        leaveunfinished=True, supptrwarns=True)
    except error.InMemoryMergeConflictsError:
        ui.status(_('hit a merge conflict\n'))

+ if confirm:
+ # abort as in-memory merge doesn't support conflict
+ rbsrt._prepareabortorcontinue(isabort=True, backup=False,
+ suppwarns=True)
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):
+ _dorebase(ui, repo, opts, inmemory=False)

    return 1
else:
  • ui.status(_('dry-run rebase completed successfully; run without '
  • '-n/--dry-run to perform this rebase\n'))

+ if confirm:
+ ui.status(_('rebase completed successfully\n'))
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):

If KeyboardInterrupt is raised here for example, abort() wouldn't be called.

finally:
  • # no need to store backup in case of dryrun
  • rbsrt._prepareabortorcontinue(isabort=True, backup=False,
  • suppwarns=True)

+ if not confirm:
+ # no need to store backup in case of dryrun
+ rbsrt._prepareabortorcontinue(isabort=True, backup=False,
+ suppwarns=True)

In D3870#61079, @yuja wrote:

Still can't apply. Which revision is this patch based on?

Ah, this patch is based on https://phab.mercurial-scm.org/D3830 which is not reviewed. Do I need to rebase this to hg-commited current tip?

with repo.wlock(), repo.lock():
    try:
        overrides = {('rebase', 'singletransaction'): True}
        with ui.configoverride(overrides, 'rebase'):
            _origrebase(ui, repo, opts, rbsrt, inmemory=True,
                        leaveunfinished=True, supptrwarns=True)
    except error.InMemoryMergeConflictsError:
        ui.status(_('hit a merge conflict\n'))

+ if confirm:
+ # abort as in-memory merge doesn't support conflict
+ rbsrt._prepareabortorcontinue(isabort=True, backup=False,
+ suppwarns=True)
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):
+ _dorebase(ui, repo, opts, inmemory=False)

    return 1
else:
  • ui.status(_('dry-run rebase completed successfully; run without '
  • '-n/--dry-run to perform this rebase\n'))

+ if confirm:
+ ui.status(_('rebase completed successfully\n'))
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):

If KeyboardInterrupt is raised here for example, abort() wouldn't be called.

okay, can I update this patch or send a follow-up to handle KeyboardInterrupt?

finally:
  • # no need to store backup in case of dryrun
  • rbsrt._prepareabortorcontinue(isabort=True, backup=False,
  • suppwarns=True)

+ if not confirm:
+ # no need to store backup in case of dryrun
+ rbsrt._prepareabortorcontinue(isabort=True, backup=False,
+ suppwarns=True)

yuja added a comment.Jul 11 2018, 9:46 AM
Ah, this patch is based on https://phab.mercurial-scm.org/D3830 which is
not reviewed. Do I need to rebase this to hg-commited current tip?

Can you rewrite this to not depend on D3830 if that's easy? It appears
nobody reviewed D3830 yet, and I can't trivially say it's good to go.

> If KeyboardInterrupt is raised here for example, abort() wouldn't be called.
okay, can I update this patch or send a follow-up to handle KeyboardInterrupt?

Can you fix that in this patch? KeyboardInterrupt is just an example. The
abort in the finally clause can't be gated by the confirm flag.

khanchi97 updated this revision to Diff 9538.Jul 11 2018, 1:17 PM
khanchi97 added inline comments.Jul 11 2018, 1:20 PM
hgext/rebase.py
895–898

@yuja will this work? Now instead of using "confirm" flag, I added "needsabort" variable.

khanchi97 added inline comments.Jul 11 2018, 1:24 PM
hgext/rebase.py
895–898

And I rebased this revision to not depend on D3830.

This revision was automatically updated to reflect the committed changes.
yuja added a comment.Jul 12 2018, 9:14 AM

Queued, thanks.

  • ui.status(_('starting dry-run rebase; repository will not be changed\n'))

+ confirm = opts.get('confirm')
+ if confirm:
+ ui.status(_('starting rebase...\n'))

Nit: this message is misleading since nothing will be committed until
accepting the changes. Can you remove it? Or alternatively, we can say that
you're doing rebase in-memory so you're safe.

with repo.wlock(), repo.lock():
    try:

+ needsabort = True

Moved this before the "try".

except error.InMemoryMergeConflictsError:
    ui.status(_('hit a merge conflict\n'))

+ if confirm:
+ # abort as in-memory merge doesn't support conflict
+ rbsrt._prepareabortorcontinue(isabort=True, backup=False,
+ suppwarns=True)
+ needsabort = False
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):

Nit: This isn't actually "apply changes".

+ _dorebase(ui, repo, opts, inmemory=False)

return _dorebase(...) to not loose the actual result code. Can you
fix by a follow-up patch?

except error.InMemoryMergeConflictsError:
    ui.status(_('hit a merge conflict\n'))

+ if confirm:
+ # abort as in-memory merge doesn't support conflict
+ rbsrt._prepareabortorcontinue(isabort=True, backup=False,
+ suppwarns=True)
+ needsabort = False
+ if not ui.promptchoice(_(b'apply changes (yn)?'
+ b'$$ &Yes $$ &No')):

Nit: This isn't actually "apply changes".

Hmm, then what it should be?

yuja added a comment.Jul 13 2018, 8:29 AM
>> +            if confirm:
>>  +                # abort as in-memory merge doesn't support conflict
>>  +                rbsrt._prepareabortorcontinue(isabort=True, backup=False,
>>  +                                              suppwarns=True)
>>  +                needsabort = False
>>  +                if not ui.promptchoice(_(b'apply changes (yn)?'
>>  +                                         b'$$ &Yes $$ &No')):
> 
> Nit: This isn't actually "apply changes".
Hmm, then what it should be?

I think we'll need to tell that we'll rerun the rebase that's known to have
at least one merge conflict. "apply changes" sounds like it would just
finalize the rebase up to the previous revision of the merge conflict.

Alternatively, it can just abort without the prompt since the dry-run was
incomplete.