Merge with crew
authorMatt Mackall <mpm@selenic.com>
Sat, 13 Jun 2009 18:08:51 -0500
changeset 8806 14a0bdd59848
parent 8801 28eaf6f8abce (diff)
parent 8805 2726a6df11e9 (current diff)
child 8807 8bf6eb68ddaf
Merge with crew
mercurial/commands.py
--- a/hgext/mq.py	Sat Jun 13 23:02:51 2009 +0200
+++ b/hgext/mq.py	Sat Jun 13 18:08:51 2009 -0500
@@ -902,14 +902,14 @@
     def push(self, repo, patch=None, force=False, list=False,
              mergeq=None, all=False):
         wlock = repo.wlock()
-        if repo.dirstate.parents()[0] not in repo.heads():
-            self.ui.status(_("(working directory not at a head)\n"))
+        try:
+            if repo.dirstate.parents()[0] not in repo.heads():
+                self.ui.status(_("(working directory not at a head)\n"))
 
-        if not self.series:
-            self.ui.warn(_('no patches in series\n'))
-            return 0
+            if not self.series:
+                self.ui.warn(_('no patches in series\n'))
+                return 0
 
-        try:
             patch = self.lookup(patch)
             # Suppose our series file is: A B C and the current 'top'
             # patch is B. qpush C should be performed (moving forward)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/share.py	Sat Jun 13 18:08:51 2009 -0500
@@ -0,0 +1,31 @@
+# Mercurial extension to provide the 'hg share' command
+#
+# Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+import os
+from mercurial.i18n import _
+from mercurial import hg, commands
+
+def share(ui, source, dest, noupdate=False):
+    """create a new shared repository (experimental)
+
+    Initialize a new repository and working directory that shares its
+    history with another repository.
+
+    NOTE: actions that change history such as rollback or moving the
+    source may confuse sharers.
+    """
+
+    return hg.share(ui, source, dest, not noupdate)
+
+cmdtable = {
+    "share":
+    (share,
+     [('U', 'noupdate', None, _('do not create a working copy'))],
+     _('[-U] SOURCE DEST')),
+}
+
+commands.norepo += " share"
--- a/mercurial/cmdutil.py	Sat Jun 13 23:02:51 2009 +0200
+++ b/mercurial/cmdutil.py	Sat Jun 13 18:08:51 2009 -0500
@@ -101,10 +101,10 @@
 def remoteui(src, opts):
     'build a remote ui from ui or repo and opts'
     if hasattr(src, 'baseui'): # looks like a repository
-        dst = src.baseui # drop repo-specific config
+        dst = src.baseui.copy() # drop repo-specific config
         src = src.ui # copy target options from repo
     else: # assume it's a global ui object
-        dst = src # keep all global options
+        dst = src.copy() # keep all global options
 
     # copy ssh-specific options
     for o in 'ssh', 'remotecmd':
--- a/mercurial/commands.py	Sat Jun 13 23:02:51 2009 +0200
+++ b/mercurial/commands.py	Sat Jun 13 18:08:51 2009 -0500
@@ -448,8 +448,12 @@
     """
     hexfunc = ui.debugflag and hex or short
     activebranches = [encoding.tolocal(repo[n].branch())
-                            for n in repo.heads(closed=False)]
-    branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
+                            for n in repo.heads()]
+    def testactive(tag, node):
+        realhead = tag in activebranches
+        open = node in repo.branchheads(tag, closed=False)
+        return realhead and open
+    branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
                           for tag, node in repo.branchtags().items()],
                       reverse=True)
 
@@ -1304,17 +1308,22 @@
 
     With no arguments, show all repository head changesets.
 
-    If branch names or revisions are given this will show the heads of
-    the specified branches or the branches those revisions are tagged
-    with.
-
     Repository "heads" are changesets that don't have child
     changesets. They are where development generally takes place and
     are the usual targets for update and merge operations.
 
-    Branch heads are changesets that have a given branch tag, but have
-    no child changesets with that tag. They are usually where
-    development on a given branch takes place.
+    If one or more REV is given, the "branch heads" will be shown for
+    the named branch associated with that revision. The name of the
+    branch is called the revision's branch tag.
+
+    Branch heads are revisions on a given named branch that do not have
+    any children on the same branch. A branch head could be a true head
+    or it could be the last changeset on a branch before a new branch
+    was created. If none of the branch heads are true heads, the branch
+    is considered inactive.
+
+    If STARTREV is specified only those heads (or branch heads) that
+    are descendants of STARTREV will be displayed.
     """
     if opts.get('rev'):
         start = repo.lookup(opts['rev'])
@@ -1324,10 +1333,10 @@
     hideinactive, _heads = opts.get('active'), None
     if not branchrevs:
         # Assume we're looking repo-wide heads if no revs were specified.
-        heads = repo.heads(start, closed=closed)
+        heads = repo.heads(start)
     else:
         if hideinactive:
-            _heads = repo.heads(start, closed=closed)
+            _heads = repo.heads(start)
         heads = []
         visitedset = set()
         for branchrev in branchrevs:
@@ -1337,7 +1346,9 @@
             visitedset.add(branch)
             bheads = repo.branchheads(branch, start, closed=closed)
             if not bheads:
-                if branch != branchrev:
+                if not opts.get('rev'):
+                    ui.warn(_("no open branch heads on branch %s\n") % branch)
+                elif branch != branchrev:
                     ui.warn(_("no changes on branch %s containing %s are "
                               "reachable from %s\n")
                             % (branch, branchrev, opts.get('rev')))
@@ -3253,7 +3264,7 @@
           ('c', 'closed', False,
            _('show normal and closed heads')),
          ] + templateopts,
-         _('[-r REV] [REV]...')),
+         _('[-r STARTREV] [REV]...')),
     "help": (help_, [], _('[TOPIC]')),
     "identify|id":
         (identify,
--- a/mercurial/hg.py	Sat Jun 13 23:02:51 2009 +0200
+++ b/mercurial/hg.py	Sat Jun 13 18:08:51 2009 -0500
@@ -81,6 +81,64 @@
         return path[5:]
     return path
 
+def share(ui, source, dest, update=True):
+    '''create a shared repository'''
+
+    if not islocal(source):
+        raise util.Abort(_('can only share local repositories'))
+
+    if isinstance(source, str):
+        origsource = ui.expandpath(source)
+        source, rev, checkout = parseurl(origsource, '')
+        srcrepo = repository(ui, source)
+    else:
+        srcrepo = source
+        origsource = source = srcrepo.url()
+        checkout = None
+
+    sharedpath = srcrepo.sharedpath # if our source is already sharing
+
+    root = os.path.realpath(dest)
+    roothg = os.path.join(root, '.hg')
+
+    if os.path.exists(roothg):
+        raise util.Abort(_('destination already exists'))
+
+    if not os.path.isdir(root):
+        os.mkdir(root)
+    os.mkdir(roothg)
+
+    requirements = ''
+    try:
+        requirements = srcrepo.opener('requires').read()
+    except IOError, inst:
+        if inst.errno != errno.ENOENT:
+            raise
+
+    requirements += 'shared\n'
+    file(os.path.join(roothg, 'requires'), 'w').write(requirements)
+    file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath)
+
+    default = srcrepo.ui.config('paths', 'default')
+    if default:
+        f = file(os.path.join(roothg, 'hgrc'), 'w')
+        f.write('[paths]\ndefault = %s\n' % default)
+        f.close()
+
+    r = repository(ui, root)
+
+    if update:
+        r.ui.status(_("updating working directory\n"))
+        if update is not True:
+            checkout = update
+        for test in (checkout, 'default', 'tip'):
+            try:
+                uprev = r.lookup(test)
+                break
+            except:
+                continue
+        _update(r, uprev)
+
 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
           stream=False):
     """Make a copy of an existing repository.
--- a/mercurial/hgweb/webcommands.py	Sat Jun 13 23:02:51 2009 +0200
+++ b/mercurial/hgweb/webcommands.py	Sat Jun 13 18:08:51 2009 -0500
@@ -361,7 +361,7 @@
 def branches(web, req, tmpl):
     b = web.repo.branchtags()
     tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
-    open = set(web.repo[n].branch() for n in web.repo.heads(closed=False))
+    heads = web.repo.heads()
     parity = paritygen(web.stripecount)
     sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
 
@@ -371,7 +371,12 @@
             if limit > 0 and count >= limit:
                 return
             count += 1
-            status = ctx.branch() in open and 'open' or 'closed'
+            if ctx.node() not in heads:
+                status = 'inactive'
+            elif not web.repo.branchheads(ctx.branch()):
+                status = 'closed'
+            else:
+                status = 'open'
             yield {'parity': parity.next(),
                    'branch': ctx.branch(),
                    'status': status,
--- a/mercurial/localrepo.py	Sat Jun 13 23:02:51 2009 +0200
+++ b/mercurial/localrepo.py	Sat Jun 13 18:08:51 2009 -0500
@@ -19,7 +19,7 @@
 
 class localrepository(repo.repository):
     capabilities = set(('lookup', 'changegroupsubset', 'branchmap'))
-    supported = set('revlogv1 store fncache'.split())
+    supported = set('revlogv1 store fncache shared'.split())
 
     def __init__(self, baseui, path=None, create=0):
         repo.repository.__init__(self)
@@ -28,6 +28,14 @@
         self.origroot = path
         self.opener = util.opener(self.path)
         self.wopener = util.opener(self.root)
+        self.baseui = baseui
+        self.ui = baseui.copy()
+
+        try:
+            self.ui.readconfig(self.join("hgrc"), self.root)
+            extensions.loadall(self.ui)
+        except IOError:
+            pass
 
         if not os.path.isdir(self.path):
             if create:
@@ -35,10 +43,10 @@
                     os.mkdir(path)
                 os.mkdir(self.path)
                 requirements = ["revlogv1"]
-                if baseui.configbool('format', 'usestore', True):
+                if self.ui.configbool('format', 'usestore', True):
                     os.mkdir(os.path.join(self.path, "store"))
                     requirements.append("store")
-                    if baseui.configbool('format', 'usefncache', True):
+                    if self.ui.configbool('format', 'usefncache', True):
                         requirements.append("fncache")
                     # create an invalid changelog
                     self.opener("00changelog.i", "a").write(
@@ -64,20 +72,23 @@
             for r in requirements - self.supported:
                 raise error.RepoError(_("requirement '%s' not supported") % r)
 
-        self.store = store.store(requirements, self.path, util.opener)
+        self.sharedpath = self.path
+        try:
+            s = os.path.realpath(self.opener("sharedpath").read())
+            if not os.path.exists(s):
+                raise error.RepoError(
+                    _('.hg/sharedpath points to nonexistent directory %s' % s))
+            self.sharedpath = s
+        except IOError, inst:
+            if inst.errno != errno.ENOENT:
+                raise
+
+        self.store = store.store(requirements, self.sharedpath, util.opener)
         self.spath = self.store.path
         self.sopener = self.store.opener
         self.sjoin = self.store.join
         self.opener.createmode = self.store.createmode
 
-        self.baseui = baseui
-        self.ui = baseui.copy()
-        try:
-            self.ui.readconfig(self.join("hgrc"), self.root)
-            extensions.loadall(self.ui)
-        except IOError:
-            pass
-
         self.tagscache = None
         self._tagstypecache = None
         self.branchcache = None
@@ -1134,15 +1145,10 @@
             finally:
                 wlock.release()
 
-    def heads(self, start=None, closed=False):
+    def heads(self, start=None):
         heads = self.changelog.heads(start)
-        def display(head):
-            if closed:
-                return True
-            extras = self.changelog.read(head)[5]
-            return ('close' not in extras)
         # sort the output in rev descending order
-        heads = [(-self.changelog.rev(h), h) for h in heads if display(h)]
+        heads = [(-self.changelog.rev(h), h) for h in heads]
         return [n for (r, n) in sorted(heads)]
 
     def branchheads(self, branch=None, start=None, closed=False):
--- a/tests/test-branches	Sat Jun 13 23:02:51 2009 +0200
+++ b/tests/test-branches	Sat Jun 13 18:08:51 2009 -0500
@@ -71,9 +71,19 @@
 echo '--- b branch should be inactive'
 hg branches
 hg branches -a
+hg heads b
+hg heads --closed b
 echo 'xxx4' >> b
 hg commit -d '9 0' -m 'reopen branch with a change'
 echo '--- branch b is back in action'
 hg branches -a
-hg heads -c
+echo '---- test heads listings'
 hg heads
+echo '% branch default'
+hg heads default
+echo '% branch a'
+hg heads a
+hg heads --active a
+echo '% branch b'
+hg heads b
+hg heads --closed b
--- a/tests/test-branches.out	Sat Jun 13 23:02:51 2009 +0200
+++ b/tests/test-branches.out	Sat Jun 13 18:08:51 2009 -0500
@@ -113,9 +113,25 @@
 a                              5:d8cbc61dbaa6 (inactive)
 default                        0:19709c5a4e75 (inactive)
 a branch name much longer than the default justification used by branches 7:10ff5895aa57
+no open branch heads on branch b
+changeset:   12:2da6583810df
+branch:      b
+tag:         tip
+parent:      8:eebb944467c9
+user:        test
+date:        Thu Jan 01 00:00:09 1970 +0000
+summary:     close this part branch too
+
+changeset:   11:c84627f3c15d
+branch:      b
+user:        test
+date:        Thu Jan 01 00:00:09 1970 +0000
+summary:     prune bad branch
+
 --- branch b is back in action
 b                             13:6ac12926b8c3
 a branch name much longer than the default justification used by branches 7:10ff5895aa57
+---- test heads listings
 changeset:   13:6ac12926b8c3
 branch:      b
 tag:         tip
@@ -135,6 +151,21 @@
 date:        Thu Jan 01 00:00:06 1970 +0000
 summary:     Adding d branch
 
+% branch default
+changeset:   0:19709c5a4e75
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     Adding root node
+
+% branch a
+changeset:   5:d8cbc61dbaa6
+branch:      a
+parent:      2:881fe2b92ad0
+user:        test
+date:        Thu Jan 01 00:00:04 1970 +0000
+summary:     Adding b branch head 2
+
+% branch b
 changeset:   13:6ac12926b8c3
 branch:      b
 tag:         tip
@@ -142,9 +173,16 @@
 date:        Thu Jan 01 00:00:09 1970 +0000
 summary:     reopen branch with a change
 
-changeset:   7:10ff5895aa57
-branch:      a branch name much longer than the default justification used by branches
+changeset:   13:6ac12926b8c3
+branch:      b
+tag:         tip
 user:        test
-date:        Thu Jan 01 00:00:06 1970 +0000
-summary:     Adding d branch
+date:        Thu Jan 01 00:00:09 1970 +0000
+summary:     reopen branch with a change
 
+changeset:   11:c84627f3c15d
+branch:      b
+user:        test
+date:        Thu Jan 01 00:00:09 1970 +0000
+summary:     prune bad branch
+
--- a/tests/test-hgweb-commands.out	Sat Jun 13 23:02:51 2009 +0200
+++ b/tests/test-hgweb-commands.out	Sat Jun 13 18:08:51 2009 -0500
@@ -532,7 +532,7 @@
 <tr class="parity1">
 <td class="age"><i>many years ago</i></td>
 <td><a class="list" href="/shortlog/a4f92ed23982?style=gitweb"><b>a4f92ed23982</b></a></td>
-<td class="closed">default</td>
+<td class="inactive">default</td>
 <td class="link">
 <a href="/changeset/a4f92ed23982?style=gitweb">changeset</a> |
 <a href="/log/a4f92ed23982?style=gitweb">changelog</a> |
--- a/tests/test-mq-qpush-fail	Sat Jun 13 23:02:51 2009 +0200
+++ b/tests/test-mq-qpush-fail	Sat Jun 13 18:08:51 2009 -0500
@@ -16,6 +16,10 @@
 echo '.hgignore' >> .hgignore
 
 hg qinit
+
+echo '% test qpush on empty series'
+hg qpush
+
 hg qnew patch1
 echo >> foo
 hg qrefresh -m 'patch 1'
--- a/tests/test-mq-qpush-fail.out	Sat Jun 13 23:02:51 2009 +0200
+++ b/tests/test-mq-qpush-fail.out	Sat Jun 13 18:08:51 2009 -0500
@@ -1,4 +1,6 @@
 adding foo
+% test qpush on empty series
+no patches in series
 patch queue now empty
 applying patch1
 applying patch2