[PATCH 3 of 3] forest extension and test case
Robin Farine
robin.farine at terminus.org
Fri Dec 22 22:53:14 UTC 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