[PATCH 3 of 3] forest extension and test case

Robin Farine robin.farine at terminus.org
Fri Dec 22 16:53:14 CST 2006


# HG changeset patch
# User Robin Farine <robin.farine at terminus.org>
# Date 1166829599 -3600
# Node ID e2372eeb08bf60f98215749680942439e0518437
# Parent  1dff63c75ba0911e8dfeb6543a6a5309ee1a44a4
forest extension and test case

diff --git a/hgext/forest.py b/hgext/forest.py
new file mode 100644
--- /dev/null
+++ b/hgext/forest.py
@@ -0,0 +1,430 @@
+# Forest, an extension to work on a set of nested Mercurial trees.
+#
+# Copyright (C) 2006 by Robin Farine <robin.farine at terminus.org>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+# Repository path representation
+#
+# Repository paths stored in the filesystem representation are stored
+# in variables named 'rpath'. Repository roots in the mercurial
+# representation, stored in variables named 'root', are used in
+# snapshot files and in command output.
+
+"""Operations on trees with nested Mercurial repositories.
+
+This extension provides commands that apply to a composite tree called
+a forest. Some commands simply wrap standard Mercurial commands, such
+as 'clone' or 'status', and others involve a snapshot file.
+
+A snapshot file represents the state of a forest at a given time. It
+has the format of a ConfigParser file and lists the trees in a forest,
+each tree with the following attributes:
+
+  root          path relative to the top-level tree
+  revision      the revision the working directory is based on
+  paths         a list of (alias, location) pairs
+
+The 'fsnap' command generates or updates such a file based on a forest
+in the file system. Other commands use this information to populate a
+forest or to pull/push changes.
+"""
+
+import ConfigParser
+import os
+import re
+
+from mercurial import commands, hg, node, util
+from mercurial.i18n import gettext as _
+from mercurial.repo import RepoError
+
+cmdtable = None
+
+commands.norepo += " fclone fseed"
+
+
+def cmd_options(ui, cmd, remove=None):
+    aliases, spec = commands.findcmd(ui, cmd)
+    res = list(spec[1])
+    if remove is not None:
+        res = [opt for opt in res if opt[0] not in remove]
+    return res
+
+
+def enumerate_repos(ui, top):
+    """Generate a lexicographically sorted list of repository roots.
+
+    The roots are absolute paths in the filesystem representation.
+    """
+
+    res = list(util.walkrepos(top))
+    res.sort()
+    return res
+
+
+tree_section_re = re.compile(r"^tree(\w+)$")
+
+def tree_sections(cfg, withtop=True):
+    """Return lexicographically sorted list of tree sections."""
+
+    allsecs = cfg.sections()
+    secs = []
+    top = None
+    for s in allsecs:
+        if tree_section_re.match(s):
+            secs.append(s)
+            if cfg.get(s, "root") == ".":
+                top = s
+    if top is None:
+        raise util.Abort(_("snapshot has no entry with root '.'"))
+    secs.sort(lambda a,b: cmp(cfg.get(a, "root"), cfg.get(b, "root")))
+    # ensure that '.' comes first, regardless of sort
+    secs.remove(top)
+    if withtop:
+        secs.insert(0, top)
+    return secs
+
+
+def mq_patches_applied(rpath):
+    rpath = os.path.join(rpath, ".hg")
+    entries = os.listdir(rpath)
+    for e in entries:
+        path = os.path.join(rpath, e)
+        if os.path.isdir(path):
+            series = os.path.join(path, "series")
+            if os.path.isfile(series):
+                s = os.stat(os.path.join(path, "status"))
+                if s.st_size > 0:
+                    return True
+    return False
+
+
+class ForestSnapshot(object):
+
+    class Tree(object):
+
+        __slots__ = ('root', 'rev', 'paths')
+
+        def __init__(self, root, rev, paths={}):
+            self.root = root
+            self.rev = rev
+            self.paths = paths
+
+        def info(self, pathalias):
+            return self.root, self.rev, self.paths.get(pathalias, None)
+
+        def update(self, rev, paths):
+            self.rev = rev
+            for name, path in paths.items():
+                if self.paths.has_key(name):
+                    self.paths[name] = path
+
+        def write(self, ui, section):
+            ui.write("root = %s\n" % self.root)
+            ui.write("revision = %s\n" % self.rev)
+            ui.write("\n[%s]\n" % (section + ".paths"))
+            for name, path in self.paths.items():
+                ui.write("%s = %s\n" % (name, path))
+
+
+    __slots__ = ('rootmap', 'trees')
+
+    def __init__(self, snapfile=None):
+        self.rootmap = {}
+        self.trees = []
+        if snapfile is not None:
+            cfg = ConfigParser.RawConfigParser()
+            cfg.read([snapfile])
+            for section in tree_sections(cfg):
+                root = cfg.get(section, 'root')
+                tree = ForestSnapshot.Tree(root, cfg.get(section, 'revision'),
+                                           dict(cfg.items(section + '.paths')))
+                self.rootmap[root] = tree
+                self.trees.append(tree)
+
+    def __call__(self, ui, toprepo, func, pathalias=None, mq_check=True):
+        """Apply a function to trees matching a snapshot entry.
+
+        Call func(repo, root, path, rev, mq_applied) for each repo in
+        toprepo and its nested repositories where repo matches a
+        snapshot entry.
+        """
+
+        repo = None
+        pfx = toprepo.root
+        for t in self.trees:
+            root, rev, path = t.info(pathalias)
+            ui.write("[%s]\n" % root)
+            if pathalias is not None and path is None:
+                ui.write(_("skipped, no path alias '%s' defined\n\n")
+                         % pathalias)
+                continue
+            if repo is None:
+                repo = toprepo
+            else:
+                try:
+                    rpath = os.path.join(pfx, util.localpath(root))
+                    repo = hg.repository(ui, rpath)
+                except RepoError:
+                    ui.write(_("skipped, no valid repo found\n\n"))
+                    continue
+            func(repo, root, path, rev,
+                 mq_check and mq_patches_applied(repo.root))
+            ui.write("\n")
+
+
+    def update(self, ui, repo, mq_fatal, tip=False):
+        """Update a snapshot by scanning a forest.
+
+        If the ForestSnapshot instance to update was initialized from
+        a snapshot file, this regenerates the list of trees with their
+        current revisions but does not add any path alias to updated
+        tree entries. Newly created tree entries get all the path aliases
+        from the corresponding repository.
+        """
+
+        rootmap = {}
+        self.trees = []
+        pfxlen = len(repo.root + os.sep)
+        for rpath in enumerate_repos(ui, repo.root):
+            root = util.pconvert(rpath[pfxlen:])
+            if root == '':
+                root = '.'
+            else:
+                repo = hg.repository(ui, rpath)
+            if mq_fatal and mq_patches_applied(rpath):
+                raise util.Abort(_("'%s' has mq patches applied") % root)
+            if tip:
+                rev = 'tip'
+            else:
+                rev = node.hex(repo.dirstate.parents()[0])
+            paths = dict(repo.ui.configitems('paths'))
+            if self.rootmap.has_key(root):
+                tree = self.rootmap[root]
+                tree.update(rev, paths)
+            else:
+                tree = ForestSnapshot.Tree(root, rev, paths)
+            rootmap[root] = tree
+            self.trees.append(tree)
+        self.rootmap = rootmap
+
+    def write(self, ui):
+        index = 1
+        for t in self.trees:
+            section = 'tree' + str(index)
+            ui.write("[%s]\n" % section)
+            t.write(ui, section)
+            ui.write("\n")
+            index += 1
+
+
+def clone(ui, source, dest, **opts):
+    """Clone a local forest."""
+    dest = os.path.normpath(dest)
+
+    def doit(repo, root, path, rev, *unused):
+        if root == '.':
+            destpath = dest
+        else:
+            destpath = os.path.join(dest, util.localpath(root))
+            destpfx = os.path.dirname(destpath)
+            if not os.path.exists(destpfx):
+                os.makedirs(destpfx)
+        opts['rev'] = [rev]
+        commands.clone(ui, repo.root, destpath, **opts)
+
+    snapshot = ForestSnapshot()
+    repo = hg.repository(ui, source)
+    snapshot.update(ui, repo, True)
+    snapshot(ui, repo, doit, mq_check=False)
+
+
+def pull(ui, toprepo, snapfile, pathalias, **opts):
+    """Pull changes from remote repositories to a local forest.
+
+    Iterate over the entries in the snapshot file and, for each entry
+    matching an actual tree in the forest and with a location
+    associated with 'pathalias', pull changes from this location to
+    the tree.
+
+    Skip entries that do not match or trees for which there is no entry.
+    """
+
+    opts['force'] = None
+    opts['rev'] = []
+
+    def doit(repo, root, path, rev, mq_applied):
+        if mq_applied:
+            ui.write(_("skipped, mq patches applied\n"))
+        else:
+            commands.pull(repo.ui, repo, path, **opts)
+
+    snapshot = ForestSnapshot(snapfile)
+    snapshot(ui, toprepo, doit, pathalias)
+
+
+def push(ui, toprepo, snapfile, pathalias, **opts):
+    """Push changes in a local forest to remote destinations.
+
+    Iterate over the entries in the snapshot file and, for each entry
+    matching an actual tree in the forest and with a location
+    associated with 'pathalias', push changes from this tree to the
+    location.
+
+    Skip entries that do not match or trees for which there is no entry.
+    """
+
+    opts['force'] = None
+    opts['rev'] = []
+
+    def doit(repo, root, path, rev, mq_applied):
+        if mq_applied:
+            ui.write(_("skipped, mq patches applied\n"))
+        else:
+            commands.push(repo.ui, repo, path, **opts)
+
+    snapshot = ForestSnapshot(snapfile)
+    snapshot(ui, toprepo, doit, pathalias)
+
+
+def seed(ui, snapshot, pathalias='default', **opts):
+    """Populate a forest according to a snapshot file."""
+
+    cfg = ConfigParser.RawConfigParser()
+    cfg.read(snapshot)
+    pfx = opts['root']
+    for section in tree_sections(cfg, bool(pfx)):
+        root = cfg.get(section, 'root')
+        ui.write("[%s]\n" % root)
+        dest = os.path.normpath(os.path.join(pfx, util.localpath(root)))
+        psect = section + '.paths'
+        if not cfg.has_option(psect, pathalias):
+            ui.write(_("skipped, no path alias '%s' defined\n\n") % pathalias)
+            continue
+        source = cfg.get(psect, pathalias)
+        if os.path.exists(dest):
+            ui.write(_("skipped, destination '%s' already exists\n\n") % dest)
+            continue
+        destpfx = os.path.dirname(dest)
+        if destpfx and not os.path.exists(destpfx):
+            os.makedirs(destpfx)
+        # 'clone -r rev' not implemented for all remote repos, clone
+        # everything and then use 'update' if necessary
+        opts['rev'] = []
+        commands.clone(ui, source, dest, **opts)
+        if not opts['tip']:
+            rev = cfg.get(section, 'revision')
+            if rev and rev != 'tip' and rev != node.nullid:
+                repo = hg.repository(ui, dest)
+                commands.update(repo.ui, repo, node=rev)
+        ui.write("\n")
+
+
+def snapshot(ui, repo, snapfile=None, **opts):
+    """Generate a new or updated forest snapshot and display it."""
+
+    snapshot = ForestSnapshot(snapfile)
+    snapshot.update(ui, repo, True, **opts)
+    snapshot.write(ui)
+
+
+def status(ui, repo, *pats, **opts):
+    """Display the status of a forest of working directories."""
+
+    def doit(repo, root, path, rev, mq_applied):
+        if mq_applied:
+            ui.write("*mq*\n")
+        commands.status(repo.ui, repo, *pats, **opts)
+
+    snapshot = ForestSnapshot()
+    snapshot.update(ui, repo, False)
+    snapshot(ui, repo, doit)
+
+
+def trees(ui, repo, **opts):
+    """List the roots of the repositories."""
+
+    if opts['convert']:
+        pfxlen = len(repo.root + os.sep)
+        l = [util.pconvert(p[pfxlen:]) for p in enumerate_repos(ui, repo.root)]
+        l.remove('')
+        l.insert(0, '.')
+    else:
+        l = enumerate_repos(ui, repo.root)
+    for t in l:
+        ui.write(t + '\n')
+
+def update(ui, toprepo, snapfile=None, tip=False, **opts):
+    """Update working directories to tip or according to a snapshot file.
+
+    When the tip option is specified, the working directory of the
+    toplevel repository and of each nested repository found in the
+    local filesystem is updated to its tip. When a snapshot file is
+    specified, the working directory of each repository listed in the
+    snapshot file is updated to the revision recorded in the snapshot.
+
+    The tip option or the snapshot file are exclusive.
+    """
+
+    if snapfile is not None and tip or snapfile is None and not tip:
+        raise util.Abort(_("need either --tip or SNAPSHOT-FILE"))
+    if tip:
+        snapshot = ForestSnapshot()
+        snapshot.update(ui, toprepo, False, True)
+    else:
+        snapshot = ForestSnapshot(snapfile)
+
+    def doit(repo, root, path, rev, mq_applied):
+        if mq_applied:
+            ui.write(_("skipped, mq patches applied\n"))
+        else:
+            commands.update(repo.ui, repo, node=rev, **opts)
+
+    snapshot(ui, toprepo, doit)
+
+
+def uisetup(ui):
+    global cmdtable
+    cmdtable = {
+        "fclone" :
+            (clone,
+             cmd_options(ui, 'clone', remove=('r',)),
+             _('hg fclone [OPTIONS] SOURCE DESTINATION')),
+        "fpull" :
+            (pull,
+             cmd_options(ui, 'pull', remove=('f', 'r')),
+             _('hg fpull [OPTIONS] SNAPSHOT-FILE PATH-ALIAS')),
+        "fpush" :
+            (push,
+             cmd_options(ui, 'push', remove=('f', 'r')),
+             _('hg fpush [OPTIONS] SNAPSHOT-FILE PATH-ALIAS')),
+        "fseed" :
+            (seed,
+             [('t', 'tip', None,
+               _("use tip instead of revisions stored in the snapshot file")),
+              ('', 'root', '',
+               _("create root as well as children under <root>"))]
+             + cmd_options(ui, 'clone', remove=('r',)),
+             _('hg fseed [OPTIONS] SNAPSHOT-FILE [PATH-ALIAS]')),
+        "fsnap" :
+            (snapshot,
+             [('t', 'tip', None,
+               _("record tip instead of actual child revisions"))],
+             _('hg fsnap [OPTIONS] [SNAPSHOT-FILE]')),
+        "fstatus" :
+            (status,
+             cmd_options(ui, 'status'),
+             _('hg fstatus [OPTIONS]')),
+        "ftrees" :
+            (trees,
+             [('c', 'convert', None,
+               _("convert paths to mercurial representation"))],
+             _('hg ftrees [OPTIONS]')),
+        "fupdate" :
+            (update,
+             [('', 'tip', False,
+               _("update working directories to a specified revision"))]
+             + cmd_options(ui, 'update', remove=('d',)),
+             _('hg fupdate (--tip | SNAPSHOT-FILE)'))
+        }
diff --git a/tests/test-forest b/tests/test-forest
new file mode 100755
--- /dev/null
+++ b/tests/test-forest
@@ -0,0 +1,133 @@
+#! /bin/sh
+
+HGRCPATH=$HGTMP/.hgrc
+export HGRCPATH
+echo "[extensions]" >> $HGRCPATH
+echo "mq=" >> $HGRCPATH
+echo "forest=" >> $HGRCPATH
+
+echo "# setup initial forest"
+hg init toplevel
+echo "f" > toplevel/f
+mkdir toplevel/d
+echo "d/f" > toplevel/d/f
+mkdir toplevel/d/d
+echo "d/d/f" > toplevel/d/d/f
+hg init toplevel/d/d/t
+echo "d/d/t/f" > toplevel/d/d/t/f
+hg init toplevel/t
+echo "t/f" > toplevel/t/f
+hg init toplevel/t/t
+echo "t/t/f" > toplevel/t/t/f
+mkdir toplevel/e
+hg init toplevel/e/d
+echo "e/d/f" > toplevel/e/d/f
+hg commit --cwd toplevel -A -m "start" -d "0 0"
+hg commit --cwd toplevel/d/d/t -A -m "start" -d "0 0"
+hg commit --cwd toplevel/t -A -m "start" -d "0 0"
+hg commit --cwd toplevel/t/t -A -m "start" -d "0 0"
+hg commit --cwd toplevel/e/d -A -m "start" -d "0 0"
+
+echo "# ftrees"
+hg ftrees --convert --cwd toplevel
+
+echo "# fstatus"
+echo "x" >> toplevel/d/d/t/f
+echo "new" >> toplevel/t/t/f2
+hg fstatus --cwd toplevel
+hg revert --cwd toplevel/d/d/t --no-backup  f
+rm -f toplevel/t/t/f2
+hg fstatus --cwd toplevel
+
+echo "# fclone"
+hg fclone toplevel topcopy
+hg fsnap --cwd topcopy > top-snap
+
+echo "# fsnap"
+hg fsnap --cwd toplevel > top-snap1
+echo "x" >> toplevel/t/t/f
+hg commit --cwd toplevel/t/t -m "new line" -d "0 0"
+echo "f2" > toplevel/d/d/f2
+hg commit --cwd toplevel/d/d -A -m "new file" -d "0 0"
+hg fsnap -R toplevel > top-snap2
+diff -u top-snap1 top-snap2 | \
+    sed -e 's/--- top-snap1.*$/--- top-snap1/' \
+        -e 's/+++ top-snap2.*$/+++ top-snap2/'
+
+echo "# fupdate"
+hg fclone toplevel newtop > /dev/null
+hg fupdate -R newtop top-snap > /dev/null
+hg parents --cwd newtop/d/d/t
+hg parents --cwd newtop/t/t
+hg fupdate --cwd newtop --tip > /dev/null
+hg parents --cwd newtop/d/d/t
+hg parents --cwd newtop/t/t
+rm -rf newtop
+
+echo "# fseed"
+hg clone toplevel newtop
+hg fseed -R newtop top-snap default
+rm -rf newtop
+hg fseed --root newtop top-snap default >/dev/null
+hg fsnap --cwd newtop | sed "s@$HGTMP at HGTMP@g"
+rm -rf newtop
+
+echo "# fpull"
+hg fpull --cwd topcopy -u ../top-snap default | sed "s@$HGTMP at HGTMP@g"
+
+echo "# fpush"
+echo "t/t/f" > topcopy/t/t/f
+hg commit --cwd topcopy/t/t -m "delete new line" -d "0 0"
+hg remove --cwd topcopy/d/d f2
+hg commit -R topcopy -m "remove new file" -d "0 0"
+hg fpush -R topcopy top-snap default | sed "s@$HGTMP at HGTMP@g"
+
+echo "# fseed and fpull, missing section"
+cat top-snap | \
+    sed -e '/\[tree2\]/,/^$/ d' \
+        -e '/\[tree2.paths\]/,/^$/ d' > top-snap-missing
+# with --root
+hg fseed --root missing top-snap-missing default
+hg ftrees --cwd missing --convert
+rm -rf missing
+# without --root
+hg init missing
+hg fseed --cwd missing ../top-snap-missing default
+hg ftrees -R missing --convert
+# pull (should find toplevel changesets)
+hg fpull -R missing top-snap-missing default \
+    | sed "s@$HGTMP at HGTMP@g"
+rm -rf missing
+
+echo "# fseed and fpull, named section"
+cat top-snap | \
+  sed 's/\[tree2/\[treenamed/' > top-snap-named
+hg fseed --root named top-snap-named default
+hg ftrees --cwd named --convert
+# pull (should find nothing)
+hg fpull --cwd named ../top-snap-named default \
+    | sed "s@$HGTMP at HGTMP@g"
+rm -rf named
+
+# create an mq patch in topcopy/t
+hg qinit --cwd topcopy/t
+hg qnew --cwd topcopy/t mq-patch
+echo "zzz" > topcopy/t/z
+hg add --cwd topcopy/t z
+hg qrefresh --cwd topcopy/t
+
+echo "# fstatus + mq"
+hg fstatus --cwd topcopy
+
+echo "# fclone + mq"
+hg fclone topcopy newtop
+rm -rf newtop
+
+echo "# fsnap + mq"
+hg fsnap --cwd topcopy ../top-snap1
+
+echo "# fpull + mq"
+hg fpull --cwd topcopy -u ../top-snap default | sed "s@$HGTMP at HGTMP@g"
+
+echo "# fpush + mq"
+hg fpush --cwd topcopy ../top-snap default | sed "s@$HGTMP at HGTMP@g"
diff --git a/tests/test-forest.out b/tests/test-forest.out
new file mode 100644
--- /dev/null
+++ b/tests/test-forest.out
@@ -0,0 +1,418 @@
+# setup initial forest
+adding d/d/f
+adding d/f
+adding f
+adding f
+adding f
+adding f
+adding f
+# ftrees
+.
+d/d/t
+e/d
+t
+t/t
+# fstatus
+[.]
+
+[d/d/t]
+M f
+
+[e/d]
+
+[t]
+
+[t/t]
+? f2
+
+[.]
+
+[d/d/t]
+
+[e/d]
+
+[t]
+
+[t/t]
+
+# fclone
+[.]
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 3 changes to 3 files
+3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[d/d/t]
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[e/d]
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t]
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t/t]
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+# fsnap
+adding d/d/f2
+--- top-snap1
++++ top-snap2
+@@ -1,6 +1,6 @@
+ [tree1]
+ root = .
+-revision = fccf42f55033a9715e9e990fcc1749e3d0d19d39
++revision = bc7d06dbb331e93b327d848dc724e61cd2dc2d66
+ 
+ [tree1.paths]
+ 
+@@ -24,7 +24,7 @@
+ 
+ [tree5]
+ root = t/t
+-revision = 5d60830890a20c050332e222b8307bbb70940a3f
++revision = e7ef7301b2ddca4eca0c4e80fe0cc8c943d05645
+ 
+ [tree5.paths]
+ 
+# fupdate
+changeset:   0:11d08ba64b67
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     start
+
+changeset:   0:5d60830890a2
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     start
+
+changeset:   0:11d08ba64b67
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     start
+
+changeset:   1:e7ef7301b2dd
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     new line
+
+# fseed
+4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+[d/d/t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[e/d]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t/t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[tree1]
+root = .
+revision = fccf42f55033a9715e9e990fcc1749e3d0d19d39
+
+[tree1.paths]
+default = HGTMP/test-forest/toplevel
+
+[tree2]
+root = d/d/t
+revision = 11d08ba64b676ed2f87a16089f3a0e5060c7bc36
+
+[tree2.paths]
+default = HGTMP/test-forest/toplevel/d/d/t
+
+[tree3]
+root = e/d
+revision = 87ae3032128b3306de675b7b390bc7b594ed74eb
+
+[tree3.paths]
+default = HGTMP/test-forest/toplevel/e/d
+
+[tree4]
+root = t
+revision = 37c7c7838b045dddb0718588a6318f002f0bed0a
+
+[tree4.paths]
+default = HGTMP/test-forest/toplevel/t
+
+[tree5]
+root = t/t
+revision = 5d60830890a20c050332e222b8307bbb70940a3f
+
+[tree5.paths]
+default = HGTMP/test-forest/toplevel/t/t
+
+# fpull
+[.]
+pulling from HGTMP/test-forest/toplevel
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[d/d/t]
+pulling from HGTMP/test-forest/toplevel/d/d/t
+searching for changes
+no changes found
+
+[e/d]
+pulling from HGTMP/test-forest/toplevel/e/d
+searching for changes
+no changes found
+
+[t]
+pulling from HGTMP/test-forest/toplevel/t
+searching for changes
+no changes found
+
+[t/t]
+pulling from HGTMP/test-forest/toplevel/t/t
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+# fpush
+[.]
+pushing to HGTMP/test-forest/toplevel
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 0 changes to 0 files
+
+[d/d/t]
+pushing to HGTMP/test-forest/toplevel/d/d/t
+searching for changes
+no changes found
+
+[e/d]
+pushing to HGTMP/test-forest/toplevel/e/d
+searching for changes
+no changes found
+
+[t]
+pushing to HGTMP/test-forest/toplevel/t
+searching for changes
+no changes found
+
+[t/t]
+pushing to HGTMP/test-forest/toplevel/t/t
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+
+# fseed and fpull, missing section
+[.]
+3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[e/d]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t/t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+.
+e/d
+t
+t/t
+[e/d]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t/t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+.
+e/d
+t
+t/t
+[.]
+pulling from HGTMP/test-forest/toplevel
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 3 changesets with 4 changes to 4 files
+(run 'hg update' to get a working copy)
+
+[e/d]
+pulling from HGTMP/test-forest/toplevel/e/d
+searching for changes
+no changes found
+
+[t]
+pulling from HGTMP/test-forest/toplevel/t
+searching for changes
+no changes found
+
+[t/t]
+pulling from HGTMP/test-forest/toplevel/t/t
+searching for changes
+no changes found
+
+# fseed and fpull, named section
+[.]
+3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[d/d/t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[e/d]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+[t/t]
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+.
+d/d/t
+e/d
+t
+t/t
+[.]
+pulling from HGTMP/test-forest/toplevel
+searching for changes
+no changes found
+
+[d/d/t]
+pulling from HGTMP/test-forest/toplevel/d/d/t
+searching for changes
+no changes found
+
+[e/d]
+pulling from HGTMP/test-forest/toplevel/e/d
+searching for changes
+no changes found
+
+[t]
+pulling from HGTMP/test-forest/toplevel/t
+searching for changes
+no changes found
+
+[t/t]
+pulling from HGTMP/test-forest/toplevel/t/t
+searching for changes
+no changes found
+
+# fstatus + mq
+[.]
+
+[d/d/t]
+
+[e/d]
+
+[t]
+*mq*
+
+[t/t]
+
+# fclone + mq
+abort: 't' has mq patches applied
+# fsnap + mq
+abort: 't' has mq patches applied
+# fpull + mq
+[.]
+pulling from HGTMP/test-forest/toplevel
+searching for changes
+no changes found
+
+[d/d/t]
+pulling from HGTMP/test-forest/toplevel/d/d/t
+searching for changes
+no changes found
+
+[e/d]
+pulling from HGTMP/test-forest/toplevel/e/d
+searching for changes
+no changes found
+
+[t]
+skipped, mq patches applied
+
+[t/t]
+pulling from HGTMP/test-forest/toplevel/t/t
+searching for changes
+no changes found
+
+# fpush + mq
+[.]
+pushing to HGTMP/test-forest/toplevel
+searching for changes
+no changes found
+
+[d/d/t]
+pushing to HGTMP/test-forest/toplevel/d/d/t
+searching for changes
+no changes found
+
+[e/d]
+pushing to HGTMP/test-forest/toplevel/e/d
+searching for changes
+no changes found
+
+[t]
+skipped, mq patches applied
+
+[t/t]
+pushing to HGTMP/test-forest/toplevel/t/t
+searching for changes
+no changes found
+


More information about the Mercurial-devel mailing list