diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -1812,11 +1812,12 @@ def forget(ui, repo, match, prefix, expl forgot.extend(forget) return bad, forgot -def cat(ui, repo, ctx, matcher, **opts): +def cat(ui, repo, ctx, matcher, prefix, **opts): err = 1 def write(path): - fp = makefileobj(repo, opts.get('output'), ctx.node(), pathname=path) + fp = makefileobj(repo, opts.get('output'), ctx.node(), + pathname=os.path.join(prefix, path)) data = ctx[path].data() if opts.get('decode'): data = repo.wwritedata(path, data) @@ -1833,9 +1834,35 @@ def cat(ui, repo, ctx, matcher, **opts): write(file) return 0 + # Don't warn about "missing" files that are really in subrepos + bad = matcher.bad + + def badfn(path, msg): + for subpath in ctx.substate: + if path.startswith(subpath): + return + bad(path, msg) + + matcher.bad = badfn + for abs in ctx.walk(matcher): write(abs) err = 0 + + matcher.bad = bad + + for subpath in sorted(ctx.substate): + sub = ctx.sub(subpath) + try: + submatch = matchmod.narrowmatcher(subpath, matcher) + + if not sub.cat(ui, submatch, os.path.join(prefix, sub._path), + **opts): + err = 0 + except error.RepoLookupError: + ui.status(_("skipping missing subrepository: %s\n") + % os.path.join(prefix, subpath)) + return err def duplicatecopies(repo, rev, fromrev): diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1174,7 +1174,7 @@ def cat(ui, repo, file1, *pats, **opts): ctx = scmutil.revsingle(repo, opts.get('rev')) m = scmutil.match(ctx, (file1,) + pats, opts) - return cmdutil.cat(ui, repo, ctx, m, **opts) + return cmdutil.cat(ui, repo, ctx, m, '', **opts) @command('^clone', [('U', 'noupdate', None, diff --git a/mercurial/help/subrepos.txt b/mercurial/help/subrepos.txt --- a/mercurial/help/subrepos.txt +++ b/mercurial/help/subrepos.txt @@ -84,6 +84,9 @@ Interaction with Mercurial Commands :archive: archive does not recurse in subrepositories unless -S/--subrepos is specified. +:cat: cat currently only handles exact file matches in subrepos. + Git and Subversion subrepositories are currently ignored. + :commit: commit creates a consistent snapshot of the state of the entire project and its subrepositories. If any subrepositories have been modified, Mercurial will abort. Mercurial can be made diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py --- a/mercurial/subrepo.py +++ b/mercurial/subrepo.py @@ -439,6 +439,9 @@ class abstractsubrepo(object): def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly): return [] + def cat(self, ui, match, prefix, **opts): + return 1 + def status(self, rev2, **opts): return [], [], [], [], [], [], [] @@ -609,6 +612,12 @@ class hgsubrepo(abstractsubrepo): os.path.join(prefix, self._path), explicitonly) @annotatesubrepoerror + def cat(self, ui, match, prefix, **opts): + rev = self._state[1] + ctx = self._repo[rev] + return cmdutil.cat(ui, self._repo, ctx, match, prefix, **opts) + + @annotatesubrepoerror def status(self, rev2, **opts): try: rev1 = self._state[1] diff --git a/tests/test-subrepo.t b/tests/test-subrepo.t --- a/tests/test-subrepo.t +++ b/tests/test-subrepo.t @@ -792,6 +792,19 @@ Prepare a repo with subrepo $ echo test >> sub/repo/foo $ hg ci -mtest committing subrepository sub/repo (glob) + $ hg cat sub/repo/foo + test + test + $ mkdir -p tmp/sub/repo + $ hg cat -r 0 --output tmp/%p_p sub/repo/foo + $ cat tmp/sub/repo/foo_p + test + $ mv sub/repo sub_ + $ hg cat sub/repo/baz + skipping missing subrepository: sub/repo + [1] + $ rm -rf sub/repo + $ mv sub_ sub/repo $ cd .. Create repo without default path, pull top repo, and see what happens on update