diff --git a/hgext/histedit.py b/hgext/histedit.py --- a/hgext/histedit.py +++ b/hgext/histedit.py @@ -149,6 +149,13 @@ configuration file:: [histedit] linelen = 120 # truncate rule lines at 120 characters + +``hg histedit`` attempts to automatically choose an appropriate base +revision to use. To change which base revision is used, define a +revset in your configuration file:: + + [histedit] + defaultrev = only(.) & draft() """ try: @@ -166,6 +173,7 @@ from mercurial import discovery from mercurial import error from mercurial import copies from mercurial import context +from mercurial import destutil from mercurial import exchange from mercurial import extensions from mercurial import hg @@ -786,13 +794,19 @@ def findoutgoing(ui, repo, remote=None, ('f', 'force', False, _('force outgoing even for unrelated repositories')), ('r', 'rev', [], _('first revision to be edited'), _('REV'))], - _("ANCESTOR | --outgoing [URL]")) + _("[ANCESTOR] | --outgoing [URL]")) def histedit(ui, repo, *freeargs, **opts): """interactively edit changeset history - This command edits changesets between ANCESTOR and the parent of + This command edits changesets between an ANCESTOR and the parent of the working directory. + The value from the "histedit.defaultrev" config option is used as a + revset to select the base revision when ANCESTOR is not specified. + The first revision returned by the revset is used. By default, this + selects the editable history that is unique to the ancestry of the + working directory. + With --outgoing, this edits changesets not found in the destination repository. If URL of the destination is omitted, the 'default-push' (or 'default') path will be used. @@ -917,10 +931,10 @@ def _histedit(ui, repo, state, *freeargs else: revs.extend(freeargs) if len(revs) == 0: - # experimental config: histedit.defaultrev - histeditdefault = ui.config('histedit', 'defaultrev') - if histeditdefault: - revs.append(histeditdefault) + defaultrev = destutil.desthistedit(ui, repo) + if defaultrev is not None: + revs.append(defaultrev) + 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 @@ -198,3 +198,18 @@ def destmerge(repo): else: node = _destmergebranch(repo) return repo[node].rev() + +histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())' + +def desthistedit(ui, repo): + """Default base revision to edit for `hg histedit`.""" + default = ui.config('histedit', 'defaultrev', histeditdefaultrevset) + if default: + revs = repo.revs(default) + if revs: + # The revset supplied by the user may not be in ascending order nor + # take the first revision. So do this manually. + revs.sort() + return revs.first() + + return None 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 @@ -347,3 +347,101 @@ Histedit state has been exited commit: 1 added, 1 unknown (new branch head) update: 4 new changesets (update) + $ cd .. + +Set up default base revision 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 - < 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 - < 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 - < pick 6f2f0241f119 + > pick 8cde254db839 + > EOF + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved