From c7a4eefa7e775b64916a66b52ca6c5f31e2cf5c8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 5 Mar 2009 10:32:38 -0800 Subject: [PATCH] Add repo manifest -o to save a manifest This can be useful to create a new manifest from an existing client, especially if the client wants to use the "-r" option to set each project's revision to the current commit SHA-1, making a sort of a tag file that can be used to recreate this exact state elsewhere. Signed-off-by: Shawn O. Pearce --- manifest.py | 74 +++++++++++++++++++++++++++++++++++++++++++-- project.py | 14 +++++---- subcmds/manifest.py | 43 +++++++++++++++++++++++--- 3 files changed, 118 insertions(+), 13 deletions(-) diff --git a/manifest.py b/manifest.py index 32a7e513..da2bb25f 100644 --- a/manifest.py +++ b/manifest.py @@ -18,7 +18,7 @@ import sys import xml.dom.minidom from git_config import GitConfig, IsId -from project import Project, MetaProject, R_HEADS +from project import Project, MetaProject, R_HEADS, HEAD from remote import Remote from error import ManifestParseError @@ -73,6 +73,76 @@ class Manifest(object): except OSError, e: raise ManifestParseError('cannot link manifest %s' % name) + def _RemoteToXml(self, r, doc, root): + e = doc.createElement('remote') + root.appendChild(e) + e.setAttribute('name', r.name) + e.setAttribute('fetch', r.fetchUrl) + if r.reviewUrl is not None: + e.setAttribute('review', r.reviewUrl) + if r.projectName is not None: + e.setAttribute('project-name', r.projectName) + + def Save(self, fd, peg_rev=False): + """Write the current manifest out to the given file descriptor. + """ + doc = xml.dom.minidom.Document() + root = doc.createElement('manifest') + doc.appendChild(root) + + d = self.default + sort_remotes = list(self.remotes.keys()) + sort_remotes.sort() + + for r in sort_remotes: + self._RemoteToXml(self.remotes[r], doc, root) + if self.remotes: + root.appendChild(doc.createTextNode('')) + + have_default = False + e = doc.createElement('default') + if d.remote: + have_default = True + e.setAttribute('remote', d.remote.name) + if d.revision: + have_default = True + e.setAttribute('revision', d.revision) + if have_default: + root.appendChild(e) + root.appendChild(doc.createTextNode('')) + + sort_projects = list(self.projects.keys()) + sort_projects.sort() + + for p in sort_projects: + p = self.projects[p] + e = doc.createElement('project') + root.appendChild(e) + e.setAttribute('name', p.name) + if p.relpath != p.name: + e.setAttribute('path', p.relpath) + if not d.remote or p.remote.name != d.remote.name: + e.setAttribute('remote', p.remote.name) + if peg_rev: + if self.IsMirror: + e.setAttribute('revision', + p.bare_git.rev_parse(p.revision + '^0')) + else: + e.setAttribute('revision', + p.work_git.rev_parse(HEAD + '^0')) + elif not d.revision or p.revision != d.revision: + e.setAttribute('revision', p.revision) + + for r in p.extraRemotes: + self._RemoteToXml(p.extraRemotes[r], doc, e) + for c in p.copyfiles: + ce = doc.createElement('copyfile') + ce.setAttribute('src', c.src) + ce.setAttribute('dest', c.dest) + e.appendChild(ce) + + doc.writexml(fd, '', ' ', '\n', 'UTF-8') + @property def projects(self): self._Load() @@ -324,7 +394,7 @@ class Manifest(object): if not self.IsMirror: # src is project relative; # dest is relative to the top of the tree - project.AddCopyFile(src, os.path.join(self.topdir, dest)) + project.AddCopyFile(src, dest, os.path.join(self.topdir, dest)) def _get_remote(self, node): name = node.getAttribute('remote') diff --git a/project.py b/project.py index b9792523..8ed61551 100644 --- a/project.py +++ b/project.py @@ -178,13 +178,15 @@ class DiffColoring(Coloring): class _CopyFile: - def __init__(self, src, dest): + def __init__(self, src, dest, abssrc, absdest): self.src = src self.dest = dest + self.abs_src = abssrc + self.abs_dest = absdest def _Copy(self): - src = self.src - dest = self.dest + src = self.abs_src + dest = self.abs_dest # copy file if it does not exist or is out of date if not os.path.exists(dest) or not filecmp.cmp(src, dest): try: @@ -691,11 +693,11 @@ class Project(object): self._CopyFiles() return True - def AddCopyFile(self, src, dest): + def AddCopyFile(self, src, dest, absdest): # dest should already be an absolute path, but src is project relative # make src an absolute path - src = os.path.join(self.worktree, src) - self.copyfiles.append(_CopyFile(src, dest)) + abssrc = os.path.join(self.worktree, src) + self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest)) def DownloadPatchSet(self, change_id, patch_id): """Download a single patch set of a single change to FETCH_HEAD. diff --git a/subcmds/manifest.py b/subcmds/manifest.py index 69906faa..4374a9d0 100644 --- a/subcmds/manifest.py +++ b/subcmds/manifest.py @@ -16,16 +16,21 @@ import os import sys -from command import Command +from command import PagedCommand -class Manifest(Command): +class Manifest(PagedCommand): common = False - helpSummary = "Manifest file" + helpSummary = "Manifest inspection utility" helpUsage = """ -%prog +%prog [-o {-|NAME.xml} [-r]] """ _helpDescription = """ -The repo manifest file describes the projects mapped into the client. + +With the -o option, exports the current manifest for inspection. +The manifest and (if present) local_manifest.xml are combined +together to produce a single manifest file. This file can be stored +in a Git repository for use during future 'repo init' invocations. + """ @property @@ -39,6 +44,34 @@ The repo manifest file describes the projects mapped into the client. fd.close() return help + def _Options(self, p): + p.add_option('-r', '--revision-as-HEAD', + dest='peg_rev', action='store_true', + help='Save revisions as current HEAD') + p.add_option('-o', '--output-file', + dest='output_file', + help='File to save the manifest to', + metavar='-|NAME.xml') + + def _Output(self, opt): + if opt.output_file == '-': + fd = sys.stdout + else: + fd = open(opt.output_file, 'w') + self.manifest.Save(fd, + peg_rev = opt.peg_rev) + fd.close() + if opt.output_file != '-': + print >>sys.stderr, 'Saved manifest to %s' % opt.output_file + def Execute(self, opt, args): + if args: + self.Usage() + + if opt.output_file is not None: + self._Output(opt) + return + + print >>sys.stderr, 'error: no operation to perform' print >>sys.stderr, 'error: see repo help manifest' sys.exit(1)