changeset 15716:049643a31b9a

merge with i18n
author Matt Mackall <mpm@selenic.com>
date Thu, 22 Dec 2011 15:56:17 -0600
parents cff25e4b37d2 (diff) bd280b2bf669 (current diff)
children 9cf1620e1e75
files
diffstat 17 files changed, 293 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Dec 21 11:48:18 2011 +0200
+++ b/.hgignore	Thu Dec 22 15:56:17 2011 -0600
@@ -49,3 +49,8 @@
 syntax: regexp
 ^\.pc/
 ^\.(pydev)?project
+
+# hackable windows distribution additions
+^hg-python26/
+^hg.exe$
+^hg.py$
--- a/hgext/largefiles/lfutil.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/hgext/largefiles/lfutil.py	Thu Dec 22 15:56:17 2011 -0600
@@ -79,7 +79,7 @@
     except OSError:
         # if hardlinks fail, fallback on atomic copy
         dst = util.atomictempfile(dest)
-        for chunk in util.filechunkiter(open(src)):
+        for chunk in util.filechunkiter(open(src, 'rb')):
             dst.write(chunk)
         dst.close()
         os.chmod(dest, os.stat(src).st_mode)
@@ -238,7 +238,7 @@
         link(usercachepath(repo.ui, hash), storepath(repo, hash))
     else:
         dst = util.atomictempfile(storepath(repo, hash))
-        for chunk in util.filechunkiter(open(file)):
+        for chunk in util.filechunkiter(open(file, 'rb')):
             dst.write(chunk)
         dst.close()
         util.copymode(file, storepath(repo, hash))
--- a/hgext/mq.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/hgext/mq.py	Thu Dec 22 15:56:17 2011 -0600
@@ -1765,6 +1765,9 @@
 
             diffopts = self.diffopts({'git': git})
             for r in rev:
+                if not repo[r].mutable():
+                    raise util.Abort(_('revision %d is not mutable') % r,
+                                     hint=_('see "hg help phases" for details'))
                 p1, p2 = repo.changelog.parentrevs(r)
                 n = repo.changelog.node(r)
                 if p2 != nullrev:
--- a/mercurial/cmdutil.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/cmdutil.py	Thu Dec 22 15:56:17 2011 -0600
@@ -588,16 +588,17 @@
         ctx1 = repo[node1]
         ctx2 = repo[node2]
         for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
+            tempnode2 = node2
             try:
                 if node2 is not None:
-                    node2 = ctx2.substate[subpath][1]
+                    tempnode2 = ctx2.substate[subpath][1]
             except KeyError:
                 # A subrepo that existed in node1 was deleted between node1 and
                 # node2 (inclusive). Thus, ctx2's substate won't contain that
                 # subpath. The best we can do is to ignore it.
-                node2 = None
+                tempnode2 = None
             submatch = matchmod.narrowmatcher(subpath, match)
-            sub.diff(diffopts, node2, submatch, changes=changes,
+            sub.diff(diffopts, tempnode2, submatch, changes=changes,
                      stat=stat, fp=fp, prefix=prefix)
 
 class changeset_printer(object):
--- a/mercurial/commands.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/commands.py	Thu Dec 22 15:56:17 2011 -0600
@@ -4362,7 +4362,7 @@
         c = repo['']
         subs = c.substate # only repos that are committed
         for s in sorted(subs):
-            if not c.sub(s).push(opts.get('force')):
+            if not c.sub(s).push(opts):
                 return False
     finally:
         del repo._subtoppath
--- a/mercurial/context.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/context.py	Thu Dec 22 15:56:17 2011 -0600
@@ -124,6 +124,8 @@
             # outdated cache
             del self._repo._phaserev
         return self._repo._phaserev[self._rev]
+    def mutable(self):
+        return self._repo._phaserev[self._rev] > 0
     def hidden(self):
         return self._rev in self._repo.changelog.hiddenrevs
 
@@ -803,6 +805,15 @@
             b.extend(p.bookmarks())
         return b
 
+    def phase(self):
+        phase = 1 # default phase to draft
+        for p in self.parents():
+            phase = max(phase, p.phase())
+        return phase
+
+    def hidden(self):
+        return False
+
     def children(self):
         return []
 
--- a/mercurial/discovery.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/discovery.py	Thu Dec 22 15:56:17 2011 -0600
@@ -85,12 +85,28 @@
     _common, inc, remoteheads = commoninc
 
     cl = repo.changelog
-    outg = cl.findmissing(common, revs)
+    alloutg = cl.findmissing(common, revs)
+    outg = []
+    secret = []
+    for o in alloutg:
+        if repo[o].phase() >= 2:
+            secret.append(o)
+        else:
+            outg.append(o)
 
     if not outg:
-        repo.ui.status(_("no changes found\n"))
+        if secret:
+            repo.ui.status(_("no changes to push but %i secret changesets\n")
+                           % len(secret))
+        else:
+            repo.ui.status(_("no changes found\n"))
         return None, 1, common
 
+    if secret:
+        # recompute target revs
+        revs = [ctx.node() for ctx in repo.set('heads(::(%ld))',
+                                               map(repo.changelog.rev, outg))]
+
     if not force and remoteheads != [nullid]:
         if remote.capable('branchmap'):
             # Check for each named branch if we're creating new remote heads.
--- a/mercurial/localrepo.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/localrepo.py	Thu Dec 22 15:56:17 2011 -0600
@@ -1251,8 +1251,15 @@
             self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
                       parent2=xp2, pending=p)
             self.changelog.finalize(trp)
-            # ensure the new commit is 1-phase
-            phases.retractboundary(self, 1, [n])
+            # set the new commit is proper phase
+            targetphase = self.ui.configint('phases', 'new-commit', 1)
+            if targetphase:
+                # retract boundary do not alter parent changeset.
+                # if a parent have higher the resulting phase will
+                # be compliant anyway
+                #
+                # if minimal phase was 0 we don't need to retract anything
+                phases.retractboundary(self, targetphase, [n])
             tr.close()
 
             if self._branchcache:
--- a/mercurial/phases.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/phases.py	Thu Dec 22 15:56:17 2011 -0600
@@ -34,6 +34,7 @@
             immutable shared
     public:     X        X
     draft:               X
+    secret:
 
 local commits are draft by default
 
@@ -50,6 +51,8 @@
 * pull never changes server states
 * publish and old server csets are seen as public by client
 
+* Any secret changeset seens in another repository is lowered to at least draft
+
 
 Here is the final table summing up the 49 possible usecase of phase exchange:
 
--- a/mercurial/posix.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/posix.py	Thu Dec 22 15:56:17 2011 -0600
@@ -238,6 +238,38 @@
     # Fallback to the likely inadequate Python builtin function.
     realpath = os.path.realpath
 
+if sys.platform == 'cygwin':
+    # workaround for cygwin, in which mount point part of path is
+    # treated as case sensitive, even though underlying NTFS is case
+    # insensitive.
+
+    # default mount points
+    cygwinmountpoints = sorted([
+            "/usr/bin",
+            "/usr/lib",
+            "/cygdrive",
+            ], reverse=True)
+
+    # use upper-ing as normcase as same as NTFS workaround
+    def normcase(path):
+        pathlen = len(path)
+        if (pathlen == 0) or (path[0] != os.sep):
+            # treat as relative
+            return encodingupper(path)
+
+        # to preserve case of mountpoint part
+        for mp in cygwinmountpoints:
+            if not path.startswith(mp):
+                continue
+
+            mplen = len(mp)
+            if mplen == pathlen: # mount point itself
+                return mp
+            if path[mplen] == os.sep:
+                return mp + encodingupper(path[mplen:])
+
+        return encodingupper(path)
+
 def shellquote(s):
     if os.sys.platform == 'OpenVMS':
         return '"%s"' % s
--- a/mercurial/setdiscovery.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/setdiscovery.py	Thu Dec 22 15:56:17 2011 -0600
@@ -9,6 +9,7 @@
 from node import nullid
 from i18n import _
 import random, collections, util, dagutil
+import phases
 
 def _updatesample(dag, nodes, sample, always, quicksamplesize=0):
     # if nodes is empty we scan the entire graph
@@ -99,7 +100,7 @@
     sample = ownheads
     if remote.local():
         # stopgap until we have a proper localpeer that supports batch()
-        srvheadhashes = remote.heads()
+        srvheadhashes = phases.visibleheads(remote)
         yesno = remote.known(dag.externalizeall(sample))
     elif remote.capable('batch'):
         batch = remote.batch()
--- a/mercurial/subrepo.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/subrepo.py	Thu Dec 22 15:56:17 2011 -0600
@@ -303,7 +303,7 @@
         """merge currently-saved state with the new state."""
         raise NotImplementedError
 
-    def push(self, force):
+    def push(self, opts):
         """perform whatever action is analogous to 'hg push'
 
         This may be a no-op on some systems.
@@ -519,19 +519,23 @@
         else:
             mergefunc()
 
-    def push(self, force):
+    def push(self, opts):
+        force = opts.get('force')
+        newbranch = opts.get('new_branch')
+        ssh = opts.get('ssh')
+
         # push subrepos depth-first for coherent ordering
         c = self._repo['']
         subs = c.substate # only repos that are committed
         for s in sorted(subs):
-            if not c.sub(s).push(force):
+            if not c.sub(s).push(opts):
                 return False
 
         dsturl = _abssource(self._repo, True)
         self._repo.ui.status(_('pushing subrepo %s to %s\n') %
             (subrelpath(self), dsturl))
-        other = hg.peer(self._repo.ui, {}, dsturl)
-        return self._repo.push(other, force)
+        other = hg.peer(self._repo.ui, {'ssh': ssh}, dsturl)
+        return self._repo.push(other, force, newbranch=newbranch)
 
     def outgoing(self, ui, dest, opts):
         return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
@@ -731,7 +735,7 @@
             if _updateprompt(self._ui, self, dirty, self._wcrev(), new):
                 self.get(state, False)
 
-    def push(self, force):
+    def push(self, opts):
         # push is a no-op for SVN
         return True
 
@@ -1025,7 +1029,9 @@
         else:
             mergefunc()
 
-    def push(self, force):
+    def push(self, opts):
+        force = opts.get('force')
+
         if not self._state[1]:
             return True
         if self._gitmissing():
--- a/mercurial/util.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/util.py	Thu Dec 22 15:56:17 2011 -0600
@@ -614,20 +614,18 @@
 def fspath(name, root):
     '''Get name in the case stored in the filesystem
 
-    The name is either relative to root, or it is an absolute path starting
-    with root. Note that this function is unnecessary, and should not be
+    The name should be relative to root, and be normcase-ed for efficiency.
+
+    Note that this function is unnecessary, and should not be
     called, for case-sensitive filesystems (simply because it's expensive).
 
-    Both name and root should be normcase-ed.
+    The root should be normcase-ed, too.
     '''
-    # If name is absolute, make it relative
-    if name.startswith(root):
-        l = len(root)
-        if name[l] == os.sep or name[l] == os.altsep:
-            l = l + 1
-        name = name[l:]
-
-    if not os.path.lexists(os.path.join(root, name)):
+    def find(p, contents):
+        lenp = len(p)
+        for n in contents:
+            if lenp == len(n) and normcase(n) == p:
+                return n
         return None
 
     seps = os.sep
@@ -643,18 +641,19 @@
             result.append(sep)
             continue
 
-        if dir not in _fspathcache:
-            _fspathcache[dir] = os.listdir(dir)
-        contents = _fspathcache[dir]
+        contents = _fspathcache.get(dir, None)
+        if contents is None:
+            contents = os.listdir(dir)
+            _fspathcache[dir] = contents
 
-        lenp = len(part)
-        for n in contents:
-            if lenp == len(n) and normcase(n) == part:
-                result.append(n)
-                break
-        else:
-            # Cannot happen, as the file exists!
-            result.append(part)
+        found = find(part, contents)
+        if not found:
+            # retry once for the corner case: add files after dir walking
+            contents = os.listdir(dir)
+            _fspathcache[dir] = contents
+            found = find(part, contents)
+
+        result.append(found or part)
         dir = os.path.join(dir, part)
 
     return ''.join(result)
--- a/mercurial/wireproto.py	Wed Dec 21 11:48:18 2011 +0200
+++ b/mercurial/wireproto.py	Thu Dec 22 15:56:17 2011 -0600
@@ -10,6 +10,7 @@
 from node import bin, hex
 import changegroup as changegroupmod
 import repo, error, encoding, util, store
+import phases
 
 # abstract batching support
 
@@ -449,7 +450,7 @@
     return streamres(proto.groupchunks(cg))
 
 def heads(repo, proto):
-    h = repo.heads()
+    h = phases.visibleheads(repo)
     return encodelist(h) + "\n"
 
 def hello(repo, proto):
--- a/tests/test-mq-qimport-fail-cleanup.t	Wed Dec 21 11:48:18 2011 +0200
+++ b/tests/test-mq-qimport-fail-cleanup.t	Thu Dec 22 15:56:17 2011 -0600
@@ -32,3 +32,9 @@
 
   $ hg qseries
   b.patch
+
+  $ hg pull -q -r 0 . # update phase
+  $ hg qimport -r 0
+  abort: revision 0 is not mutable
+  (see "hg help phases" for details)
+  [255]
--- a/tests/test-phases-exchange.t	Wed Dec 21 11:48:18 2011 +0200
+++ b/tests/test-phases-exchange.t	Thu Dec 22 15:56:17 2011 -0600
@@ -7,7 +7,9 @@
   $ mkcommit() {
   >    echo "$1" > "$1"
   >    hg add "$1"
-  >    hg ci -m "$1"
+  >    message="$1"
+  >    shift
+  >    hg ci -m "$message" $*
   > }
 
   $ hg init alpha
@@ -478,6 +480,7 @@
 
 Pushing to Publish=True (common changeset from publish=False)
 
+(in mu)
   $ hg push ../alpha
   pushing to ../alpha
   searching for changes
@@ -506,3 +509,62 @@
   1 0 a-B - 548a3d25dbf0
   0 0 a-A - 054250a37db4
 
+
+Discovery locally secret changeset on a remote repository:
+
+- should make it non-secret
+
+  $ cd ../alpha
+  $ mkcommit A-secret --config phases.new-commit=2
+  $ hgph
+  11 2 A-secret - 435b5d83910c
+  10 0 a-H - 967b449fbc94
+  9 1 a-G - 3e27b6f1eee1
+  8 0 a-F - b740e3e5c05d
+  7 0 a-E - e9f537e46dea
+  6 0 n-B - 145e75495359
+  5 0 n-A - d6bcb4f74035
+  4 0 b-A - f54f1bb90ff3
+  3 0 a-D - b555f63b6063
+  2 0 a-C - 54acac6f23ab
+  1 0 a-B - 548a3d25dbf0
+  0 0 a-A - 054250a37db4
+  $ hg bundle --base 'parents(.)' -r . ../secret-bundle.hg
+  1 changesets found
+  $ hg -R ../mu unbundle ../secret-bundle.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  (run 'hg update' to get a working copy)
+  $ hgph -R ../mu
+  10 1 A-secret - 435b5d83910c
+  9 0 a-H - 967b449fbc94
+  8 0 a-F - b740e3e5c05d
+  7 0 a-E - e9f537e46dea
+  6 0 n-B - 145e75495359
+  5 0 n-A - d6bcb4f74035
+  4 0 a-D - b555f63b6063
+  3 0 a-C - 54acac6f23ab
+  2 0 b-A - f54f1bb90ff3
+  1 0 a-B - 548a3d25dbf0
+  0 0 a-A - 054250a37db4
+  $ hg pull ../mu
+  pulling from ../mu
+  searching for changes
+  no changes found
+  $ hgph
+  11 1 A-secret - 435b5d83910c
+  10 0 a-H - 967b449fbc94
+  9 1 a-G - 3e27b6f1eee1
+  8 0 a-F - b740e3e5c05d
+  7 0 a-E - e9f537e46dea
+  6 0 n-B - 145e75495359
+  5 0 n-A - d6bcb4f74035
+  4 0 b-A - f54f1bb90ff3
+  3 0 a-D - b555f63b6063
+  2 0 a-C - 54acac6f23ab
+  1 0 a-B - 548a3d25dbf0
+  0 0 a-A - 054250a37db4
+
+
--- a/tests/test-phases.t	Wed Dec 21 11:48:18 2011 +0200
+++ b/tests/test-phases.t	Thu Dec 22 15:56:17 2011 -0600
@@ -2,7 +2,9 @@
   $ mkcommit() {
   >    echo "$1" > "$1"
   >    hg add "$1"
-  >    hg ci -m "$1"
+  >    message="$1"
+  >    shift
+  >    hg ci -m "$message" $*
   > }
 
   $ hg init initialrepo
@@ -37,3 +39,98 @@
   2 1 C
   1 0 B
   0 0 A
+
+Test creating changeset as secret
+
+  $ mkcommit E --config phases.new-commit=2
+  $ hglog
+  4 2 E
+  3 1 D
+  2 1 C
+  1 0 B
+  0 0 A
+
+Test the secret property is inherited
+
+  $ mkcommit H
+  $ hglog
+  5 2 H
+  4 2 E
+  3 1 D
+  2 1 C
+  1 0 B
+  0 0 A
+
+Even on merge
+
+  $ hg up -q 1
+  $ mkcommit "B'"
+  created new head
+  $ hglog
+  6 1 B'
+  5 2 H
+  4 2 E
+  3 1 D
+  2 1 C
+  1 0 B
+  0 0 A
+  $ hg merge 4 # E
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m "merge B' and E"
+  $ hglog
+  7 2 merge B' and E
+  6 1 B'
+  5 2 H
+  4 2 E
+  3 1 D
+  2 1 C
+  1 0 B
+  0 0 A
+
+Test secret changeset are not pushed
+
+  $ hg init ../push-dest
+  $ hg push ../push-dest -f # force because we push multiple heads
+  pushing to ../push-dest
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 5 changesets with 5 changes to 5 files (+1 heads)
+  $ hglog
+  7 2 merge B' and E
+  6 0 B'
+  5 2 H
+  4 2 E
+  3 0 D
+  2 0 C
+  1 0 B
+  0 0 A
+  $ cd ../push-dest
+  $ hglog
+  4 0 B'
+  3 0 D
+  2 0 C
+  1 0 B
+  0 0 A
+  $ cd ..
+
+Test secret changeset are not pull
+
+  $ hg init pull-dest
+  $ cd pull-dest
+  $ hg pull ../initialrepo
+  pulling from ../initialrepo
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 5 changesets with 5 changes to 5 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hglog
+  4 0 B'
+  3 0 D
+  2 0 C
+  1 0 B
+  0 0 A