Patchwork [sprint] histedit: attempt to choose base revision automatically

login
register
mail settings
Submitter Gregory Szorc
Date Oct. 24, 2015, 6:56 p.m.
Message ID <134a75d34a5bd5009f49.1445713015@gps-mbp.local>
Download mbox | patch
Permalink /patch/11239/
State Changes Requested
Headers show

Comments

Gregory Szorc - Oct. 24, 2015, 6:56 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1445712999 -3600
#      Sat Oct 24 19:56:39 2015 +0100
# Node ID 134a75d34a5bd5009f490ca897b7ce7227fc1bde
# Parent  a9ed5a8fc5e0554d5cb81b7206d2203cc49a2d23
histedit: attempt to choose base revision automatically

Previously, `hg histedit` required a revision argument specifying which
revision to use as the base for the current histedit operation. There
is an undocumented "histedit.defaultrev" option that supports defining a
revset to be used if no argument is passed.

histedit knows what changesets it can edit. And in most scenarios,
people want to edit the history of everything on the current head that
are rewritable. This patch changes the behavior of histedit to try to
automatically select rewritable changesets.
Matt Mackall - Nov. 1, 2015, 7:28 p.m.
On Sat, 2015-10-24 at 19:56 +0100, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1445712999 -3600
> #      Sat Oct 24 19:56:39 2015 +0100
> # Node ID 134a75d34a5bd5009f490ca897b7ce7227fc1bde
> # Parent  a9ed5a8fc5e0554d5cb81b7206d2203cc49a2d23
> histedit: attempt to choose base revision automatically

Looks fine, but..

>                # experimental config: histedit.defaultrev
>                  histeditdefault = ui.config('histedit',
> 'defaultrev')
>                  if histeditdefault:
>                      revs.append(histeditdefault)
> +                else:
> +                    # Use a sane default for the ancestor revision.
> +                    baserev = destutil.histeditbase(repo)
> +                    if baserev is None:
> +                        raise error.Abort(
> +                            _('could not determine appropriate base
> revision'))
> +                    revs = [baserev]
> +

I'm not sure why histedit.defaultrev and destutil.histeditbase are
distinct. The latter is "what histedit will do by default".. and so is
the former. The point of putting something in destutils is so it can
have more than one users The second such thing will almost surely be
broken if it doesn't manually take defaultrev into account, no? And
doing it manually defeats the purpose of having it in destutil.

-- 
Mathematics is the supreme nostalgia of our time.

Patch

diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -161,8 +161,9 @@  import os
 import sys
 
 from mercurial import bundle2
 from mercurial import cmdutil
+from mercurial import destutil
 from mercurial import discovery
 from mercurial import error
 from mercurial import copies
 from mercurial import context
@@ -778,8 +779,16 @@  def _histedit(ui, repo, state, *freeargs
                 # experimental config: histedit.defaultrev
                 histeditdefault = ui.config('histedit', 'defaultrev')
                 if histeditdefault:
                     revs.append(histeditdefault)
+                else:
+                    # Use a sane default for the ancestor revision.
+                    baserev = destutil.histeditbase(repo)
+                    if baserev is None:
+                        raise error.Abort(
+                            _('could not determine appropriate base revision'))
+                    revs = [baserev]
+
             if len(revs) != 1:
                 raise error.Abort(
                     _('histedit requires exactly one ancestor revision'))
 
diff --git a/mercurial/destutil.py b/mercurial/destutil.py
--- a/mercurial/destutil.py
+++ b/mercurial/destutil.py
@@ -197,4 +197,13 @@  def destmerge(repo):
         node = _destmergebook(repo)
     else:
         node = _destmergebranch(repo)
     return repo[node].rev()
+
+def histeditbase(repo):
+    """Default base revision for a histedit operation."""
+    revs = repo.revs('first(only(.) and not public() and not ::merge())')
+
+    if not revs:
+        return None
+
+    return revs.first()
diff --git a/tests/test-histedit-arguments.t b/tests/test-histedit-arguments.t
--- a/tests/test-histedit-arguments.t
+++ b/tests/test-histedit-arguments.t
@@ -346,4 +346,102 @@  Histedit state has been exited
   parent: 5:63379946892c 
   commit: 1 added, 1 unknown (new branch head)
   update: 4 new changesets (update)
 
+  $ cd ..
+
+Set up default base selection tests
+
+  $ hg init defaultbase
+  $ cd defaultbase
+  $ touch foo
+  $ hg -q commit -A -m root
+  $ echo 1 > foo
+  $ hg commit -m 'public 1'
+  $ hg phase --force --public -r .
+  $ echo 2 > foo
+  $ hg commit -m 'draft after public'
+  $ hg -q up -r 1
+  $ echo 3 > foo
+  $ hg commit -m 'head 1 public'
+  created new head
+  $ hg phase --force --public -r .
+  $ echo 4 > foo
+  $ hg commit -m 'head 1 draft 1'
+  $ echo 5 > foo
+  $ hg commit -m 'head 1 draft 2'
+  $ hg -q up -r 2
+  $ echo 6 > foo
+  $ hg commit -m 'head 2 commit 1'
+  $ echo 7 > foo
+  $ hg commit -m 'head 2 commit 2'
+  $ hg -q up -r 2
+  $ echo 8 > foo
+  $ hg commit -m 'head 3'
+  created new head
+  $ hg -q up -r 2
+  $ echo 9 > foo
+  $ hg commit -m 'head 4'
+  created new head
+  $ hg merge --tool :local -r 8
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg commit -m 'merge head 3 into head 4'
+  $ echo 11 > foo
+  $ hg commit -m 'commit 1 after merge'
+  $ echo 12 > foo
+  $ hg commit -m 'commit 2 after merge'
+
+  $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
+  @  12:8cde254db839 draft commit 2 after merge
+  |
+  o  11:6f2f0241f119 draft commit 1 after merge
+  |
+  o    10:90506cc76b00 draft merge head 3 into head 4
+  |\
+  | o  9:f8607a373a97 draft head 4
+  | |
+  o |  8:0da92be05148 draft head 3
+  |/
+  | o  7:4c35cdf97d5e draft head 2 commit 2
+  | |
+  | o  6:931820154288 draft head 2 commit 1
+  |/
+  | o  5:8cdc02b9bc63 draft head 1 draft 2
+  | |
+  | o  4:463b8c0d2973 draft head 1 draft 1
+  | |
+  | o  3:23a0c4eefcbf public head 1 public
+  | |
+  o |  2:4117331c3abb draft draft after public
+  |/
+  o  1:4426d359ea59 public public 1
+  |
+  o  0:54136a8ddf32 public root
+  
+
+Default base revision should stop at public changesets
+
+  $ hg -q up 8cdc02b9bc63
+  $ hg histedit --commands - <<EOF
+  > pick 463b8c0d2973
+  > pick 8cdc02b9bc63
+  > EOF
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Default base revision should stop at branchpoint
+
+  $ hg -q up 4c35cdf97d5e
+  $ hg histedit --commands - <<EOF
+  > pick 931820154288
+  > pick 4c35cdf97d5e
+  > EOF
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Default base revision should stop at merge commit
+
+  $ hg -q up 8cde254db839
+  $ hg histedit --commands - <<EOF
+  > pick 6f2f0241f119
+  > pick 8cde254db839
+  > EOF
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved