changeset 18990:7373be706f02

merge with crew
author Matt Mackall <mpm@selenic.com>
date Tue, 16 Apr 2013 13:22:29 -0500
parents a59e575c6ff8 (diff) 12a3474c1634 (current diff)
children c1af1fb314bc
files
diffstat 6 files changed, 228 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/bookmarks.py	Tue Apr 16 10:08:20 2013 -0700
+++ b/mercurial/bookmarks.py	Tue Apr 16 13:22:29 2013 -0500
@@ -292,19 +292,7 @@
         # (new != nullrev has been excluded by the previous check)
         return True
     elif repo.obsstore:
-        # We only need this complicated logic if there is obsolescence
-        # XXX will probably deserve an optimised revset.
-        nm = repo.changelog.nodemap
-        validdests = set([old])
-        plen = -1
-        # compute the whole set of successors or descendants
-        while len(validdests) != plen:
-            plen = len(validdests)
-            succs = set(c.node() for c in validdests)
-            mutable = [c.node() for c in validdests if c.mutable()]
-            succs.update(obsolete.allsuccessors(repo.obsstore, mutable))
-            known = (n for n in succs if n in nm)
-            validdests = set(repo.set('%ln::', known))
-        return new in validdests
+        return new.node() in obsolete.foreground(repo, [old.node()])
     else:
+        # still an independant clause as it is lazyer (and therefore faster)
         return old.descendant(new)
--- a/mercurial/merge.py	Tue Apr 16 10:08:20 2013 -0700
+++ b/mercurial/merge.py	Tue Apr 16 13:22:29 2013 -0500
@@ -7,6 +7,7 @@
 
 from node import nullid, nullrev, hex, bin
 from i18n import _
+from mercurial import obsolete
 import error, util, filemerge, copies, subrepo, worker, dicthelpers
 import errno, os, shutil
 
@@ -697,17 +698,26 @@
                                        "subrepository '%s'") % s)
 
         elif not overwrite:
-            if pa == p1 or pa == p2: # linear
-                pass # all good
-            elif wc.dirty(missing=True):
-                raise util.Abort(_("crosses branches (merge branches or use"
-                                   " --clean to discard changes)"))
-            elif onode is None:
-                raise util.Abort(_("crosses branches (merge branches or update"
-                                   " --check to force update)"))
-            else:
-                # Allow jumping branches if clean and specific rev given
-                pa = p1
+            if pa not in (p1, p2):  # nolinear
+                dirty = wc.dirty(missing=True)
+                if dirty or onode is None:
+                    # Branching is a bit strange to ensure we do the minimal
+                    # amount of call to obsolete.background.
+                    foreground = obsolete.foreground(repo, [p1.node()])
+                    # note: the <node> variable contains a random identifier
+                    if repo[node].node() in foreground:
+                        pa = p1  # allow updating to successors
+                    elif dirty:
+                        msg = _("crosses branches (merge branches or use"
+                                " --clean to discard changes)")
+                        raise util.Abort(msg)
+                    else:  # node is none
+                        msg = _("crosses branches (merge branches or update"
+                                " --check to force update)")
+                        raise util.Abort(msg)
+                else:
+                    # Allow jumping branches if clean and specific rev given
+                    pa = p1
 
         ### calculate phase
         actions = calculateupdates(repo, wc, p2, pa,
--- a/mercurial/obsolete.py	Tue Apr 16 10:08:20 2013 -0700
+++ b/mercurial/obsolete.py	Tue Apr 16 13:22:29 2013 -0500
@@ -405,6 +405,33 @@
                     seen.add(suc)
                     remaining.add(suc)
 
+def foreground(repo, nodes):
+    """return all nodes in the "foreground" of other node
+
+    The foreground of a revision is anything reachable using parent -> children
+    or precursor -> sucessor relation. It is very similars to "descendant" but
+    augmented with obsolescence information.
+
+    Beware that possible obsolescence cycle may result if complexe situation.
+    """
+    repo = repo.unfiltered()
+    foreground = set(repo.set('%ln::', nodes))
+    if repo.obsstore:
+        # We only need this complicated logic if there is obsolescence
+        # XXX will probably deserve an optimised revset.
+        nm = repo.changelog.nodemap
+        plen = -1
+        # compute the whole set of successors or descendants
+        while len(foreground) != plen:
+            plen = len(foreground)
+            succs = set(c.node() for c in foreground)
+            mutable = [c.node() for c in foreground if c.mutable()]
+            succs.update(allsuccessors(repo.obsstore, mutable))
+            known = (n for n in succs if n in nm)
+            foreground = set(repo.set('%ln::', known))
+    return set(c.node() for c in foreground)
+
+
 def successorssets(repo, initialnode, cache=None):
     """Return all set of successors of initial nodes
 
--- a/mercurial/phases.py	Tue Apr 16 10:08:20 2013 -0700
+++ b/mercurial/phases.py	Tue Apr 16 13:22:29 2013 -0500
@@ -266,7 +266,15 @@
                 filtered = True
         if filtered:
             self.dirty = True
-            self._phaserevs = None
+        # filterunknown is called by repo.destroyed, we may have no changes in
+        # root but phaserevs contents is certainly invalide (or at least we
+        # have not proper way to check that. related to issue 3858.
+        #
+        # The other caller is __init__ that have no _phaserevs initialized
+        # anyway. If this change we should consider adding a dedicated
+        # "destroyed" function to phasecache or a proper cache key mechanisme
+        # (see branchmap one)
+        self._phaserevs = None
 
 def advanceboundary(repo, targetphase, nodes):
     """Add nodes to a phase changing other nodes phases if necessary.
--- a/tests/test-rebase-cache.t	Tue Apr 16 10:08:20 2013 -0700
+++ b/tests/test-rebase-cache.t	Tue Apr 16 13:22:29 2013 -0500
@@ -385,3 +385,112 @@
 
   $ hg theads
   0: 'A' 
+
+Make sure rebase does not break for phase/filter related reason
+----------------------------------------------------------------
+(issue3858)
+
+  $ cd ..
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > logtemplate={rev} {desc} {phase}\n
+  > EOF
+  $ cat $HGRCPATH
+  [ui]
+  slash = True
+  interactive = False
+  [defaults]
+  backout = -d "0 0"
+  commit = -d "0 0"
+  tag = -d "0 0"
+  [extensions]
+  graphlog=
+  rebase=
+  mq=
+  
+  [phases]
+  publish=False
+  
+  [alias]
+  tglog  = log -G --template "{rev}: '{desc}' {branches}\n"
+  theads = heads --template "{rev}: '{desc}' {branches}\n"
+  [ui]
+  logtemplate={rev} {desc} {phase}\n
+
+
+  $ hg init c4
+  $ cd c4
+
+  $ echo a > a
+  $ hg ci -Am A
+  adding a
+  $ echo b > b
+  $ hg ci -Am B
+  adding b
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo c > c
+  $ hg ci -Am C
+  adding c
+  created new head
+  $ hg up 1
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg merge
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m d
+  $ hg up 2
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo e > e
+  $ hg ci -Am E
+  adding e
+  created new head
+  $ hg merge 3
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m F
+  $ hg up 3
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo g > g
+  $ hg ci -Am G
+  adding g
+  created new head
+  $ echo h > h
+  $ hg ci -Am H
+  adding h
+  $ hg up 5
+  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo i > i
+  $ hg ci -Am I
+  adding i
+
+Turn most changeset public
+
+  $ hg ph -p 7
+
+  $ hg heads
+  8 I draft
+  7 H public
+  $ hg log -G
+  @  8 I draft
+  |
+  | o  7 H public
+  | |
+  | o  6 G public
+  | |
+  o |  5 F draft
+  |\|
+  o |  4 E draft
+  | |
+  | o  3 d public
+  |/|
+  o |  2 C public
+  | |
+  | o  1 B public
+  |/
+  o  0 A public
+  
+
+  $ hg rebase --dest 7 --source 5
+  saved backup bundle to $TESTTMP/a3/c4/.hg/strip-backup/*-backup.hg (glob)
--- a/tests/test-update-branches.t	Tue Apr 16 10:08:20 2013 -0700
+++ b/tests/test-update-branches.t	Tue Apr 16 13:22:29 2013 -0500
@@ -164,3 +164,63 @@
   parent=1
   M foo
 
+Test obsolescence behavior
+---------------------------------------------------------------------
+
+successors should be taken in account when checking head destination
+
+  $ cat << EOF >> $HGRCPATH
+  > [extensions]
+  > obs=$TESTTMP/obs.py
+  > [ui]
+  > logtemplate={rev}:{node|short} {desc|firstline}
+  > EOF
+  $ cat > $TESTTMP/obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+
+Test no-argument update to a successor of an obsoleted changeset
+
+  $ hg log -G
+  o  5:ff252e8273df 5
+  |
+  o  4:d047485b3896 4
+  |
+  | o  3:6efa171f091b 3
+  | |
+  | | o  2:bd10386d478c 2
+  | |/
+  | @  1:0786582aa4b1 1
+  |/
+  o  0:60829823a42a 0
+  
+  $ hg status
+  M foo
+
+We add simple obsolescence marker between 3 and 4 (indirect successors)
+
+  $ hg id --debug -i -r 3
+  6efa171f091b00a3c35edc15d48c52a498929953
+  $ hg id --debug -i -r 4
+  d047485b3896813b2a624e86201983520f003206
+  $ hg debugobsolete 6efa171f091b00a3c35edc15d48c52a498929953 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa d047485b3896813b2a624e86201983520f003206
+
+Test that 5 is detected as a valid destination from 3
+  $ hg up --quiet --hidden 3
+  $ hg up 5
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Test that 5 is detected as a valid destination from 1
+  $ hg up --quiet 0          # we should be able to update to 3 directly
+  $ hg up --quiet --hidden 3 # but not implemented yet.
+  $ hg up 5
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Test that 5 is not detected as a valid destination from 2
+  $ hg up --quiet 0
+  $ hg up --quiet 2
+  $ hg up 5
+  abort: crosses branches (merge branches or use --clean to discard changes)
+  [255]