Exemplo n.º 1
0
    def _parse(self):
        if self.changeset is not None:
            return
        self.changeset = {}

        maxrev = 0
        if self.rev:
            # TODO: handle tags
            try:
                # patchset number?
                maxrev = int(self.rev)
            except ValueError:
                raise util.Abort(
                    _('revision %s is not a patchset number') % self.rev)

        d = os.getcwd()
        try:
            os.chdir(self.path)
            id = None

            cache = 'update'
            if not self.ui.configbool('convert', 'cvsps.cache', True):
                cache = None
            db = cvsps.createlog(self.ui, cache=cache)
            db = cvsps.createchangeset(
                self.ui,
                db,
                fuzz=int(self.ui.config('convert', 'cvsps.fuzz', 60)),
                mergeto=self.ui.config('convert', 'cvsps.mergeto', None),
                mergefrom=self.ui.config('convert', 'cvsps.mergefrom', None))

            for cs in db:
                if maxrev and cs.id > maxrev:
                    break
                id = str(cs.id)
                cs.author = self.recode(cs.author)
                self.lastbranch[cs.branch] = id
                cs.comment = self.recode(cs.comment)
                date = util.datestr(cs.date)
                self.tags.update(dict.fromkeys(cs.tags, id))

                files = {}
                for f in cs.entries:
                    files[f.file] = "%s%s" % ('.'.join(
                        [str(x) for x in f.revision]), ['', '(DEAD)'][f.dead])

                # add current commit to set
                c = commit(author=cs.author,
                           date=date,
                           parents=[str(p.id) for p in cs.parents],
                           desc=cs.comment,
                           branch=cs.branch or '')
                self.changeset[id] = c
                self.files[id] = files

            self.heads = self.lastbranch.values()
        finally:
            os.chdir(d)
Exemplo n.º 2
0
 def getcommit(self, rev):
     certs = self.mtngetcerts(rev)
     return commit(author=certs["author"],
                   date=util.datestr(
                       util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
                   desc=certs["changelog"],
                   rev=rev,
                   parents=self.mtnrun("parents", rev).splitlines(),
                   branch=certs["branch"])
 def getcommit(self, rev):
     elt = self.changes[rev]
     date = util.strdate(elt.get('local_date'), '%a %b %d %H:%M:%S %Z %Y')
     desc = elt.findtext('name') + '\n' + elt.findtext('comment', '')
     # etree can return unicode objects for name, comment, and author,
     # so recode() is used to ensure str objects are emitted.
     return commit(author=self.recode(elt.get('author')),
                   date=util.datestr(date),
                   desc=self.recode(desc).strip(),
                   parents=self.parents[rev])
Exemplo n.º 4
0
    def sendemail(self, address, data):
        p = email.Parser.Parser()
        msg = p.parsestr(data)
        msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
        msg['To'] = address
        msg['From'] = self.emailfrom
        msg['Subject'] = 'DeliverXML'
        msg['Content-type'] = 'text/xml'
        msgtext = msg.as_string()

        self.ui.status(_('hgcia: sending update to %s\n') % address)
        mail.sendmail(self.ui, util.email(self.emailfrom), [address], msgtext)
 def getcommit(self, rev):
     ctx = self.changectx(rev)
     parents = [p.hex() for p in self.parents(ctx)]
     if self.saverev:
         crev = rev
     else:
         crev = None
     return commit(author=ctx.user(),
                   date=util.datestr(ctx.date()),
                   desc=ctx.description(),
                   rev=crev,
                   parents=parents,
                   branch=ctx.branch(),
                   extra=ctx.extra(),
                   sortkey=ctx.rev())
    def _parsecatlog(self, data, rev):
        try:
            catlog = self.catlogparser.parsestr(data)

            # Commit date
            self.changes[rev].date = util.datestr(
                util.strdate(catlog['Standard-date'],
                             '%Y-%m-%d %H:%M:%S'))

            # Commit author
            self.changes[rev].author = self.recode(catlog['Creator'])

            # Commit description
            self.changes[rev].summary = '\n\n'.join((catlog['Summary'],
                                                    catlog.get_payload()))
            self.changes[rev].summary = self.recode(self.changes[rev].summary)

            # Commit revision origin when dealing with a branch or tag
            if 'Continuation-of' in catlog:
                self.changes[rev].continuationof = self.recode(
                    catlog['Continuation-of'])
        except Exception:
            raise util.Abort(_('could not parse cat-log of %s') % rev)
commands.optionalrepo += ' kwdemo'

# hg commands that do not act on keywords
nokwcommands = ('add addremove annotate bundle export grep incoming init log'
                ' outgoing push tip verify convert email glog')

# hg commands that trigger expansion only when writing to working dir,
# not when reading filelog, and unexpand when reading from working dir
restricted = 'merge kwexpand kwshrink record qrecord resolve transplant'

# names of extensions using dorecord
recordextensions = 'record'

# date like in cvs' $Date
utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
# date like in svn's $Date
svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
# date like in svn's $Id
svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ')

# make keyword tools accessible
kwtools = {'templater': None, 'hgcmd': ''}


def _defaultkwmaps(ui):
    '''Returns default keywordmaps according to keywordset configuration.'''
    templates = {
        'Revision': '{node|short}',
        'Author': '{author|user}',
    }
Exemplo n.º 8
0
        def parselogentry(orig_paths, revnum, author, date, message):
            """Return the parsed commit object or None, and True if
            the revision is a branch root.
            """
            self.ui.debug("parsing revision %d (%d changes)\n" %
                          (revnum, len(orig_paths)))

            branched = False
            rev = self.revid(revnum)
            # branch log might return entries for a parent we already have

            if rev in self.commits or revnum < to_revnum:
                return None, branched

            parents = []
            # check whether this revision is the start of a branch or part
            # of a branch renaming
            orig_paths = sorted(orig_paths.iteritems())
            root_paths = [(p, e) for p, e in orig_paths
                          if self.module.startswith(p)]
            if root_paths:
                path, ent = root_paths[-1]
                if ent.copyfrom_path:
                    branched = True
                    newpath = ent.copyfrom_path + self.module[len(path):]
                    # ent.copyfrom_rev may not be the actual last revision
                    previd = self.latest(newpath, ent.copyfrom_rev)
                    if previd is not None:
                        prevmodule, prevnum = self.revsplit(previd)[1:]
                        if prevnum >= self.startrev:
                            parents = [previd]
                            self.ui.note(
                                _('found parent of branch %s at %d: %s\n') %
                                (self.module, prevnum, prevmodule))
                else:
                    self.ui.debug("no copyfrom path, don't know what to do.\n")

            paths = []
            # filter out unrelated paths
            for path, ent in orig_paths:
                if self.getrelpath(path) is None:
                    continue
                paths.append((path, ent))

            # Example SVN datetime. Includes microseconds.
            # ISO-8601 conformant
            # '2007-01-04T17:35:00.902377Z'
            date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])

            log = message and self.recode(message) or ''
            author = author and self.recode(author) or ''
            try:
                branch = self.module.split("/")[-1]
                if branch == 'trunk':
                    branch = ''
            except IndexError:
                branch = None

            cset = commit(author=author,
                          date=util.datestr(date),
                          desc=log,
                          parents=parents,
                          branch=branch,
                          rev=rev)

            self.commits[rev] = cset
            # The parents list is *shared* among self.paths and the
            # commit object. Both will be updated below.
            self.paths[rev] = (paths, cset.parents)
            if self.child_cset and not self.child_cset.parents:
                self.child_cset.parents[:] = [rev]
            self.child_cset = cset
            return cset, branched
Exemplo n.º 9
0
    def _parse(self, ui, path):
        "Prepare list of P4 filenames and revisions to import"
        ui.status(_('reading p4 views\n'))

        # read client spec or view
        if "/" in path:
            self._parse_view(path)
            if path.startswith("//") and path.endswith("/..."):
                views = {path[:-3]:""}
            else:
                views = {"//": ""}
        else:
            cmd = 'p4 -G client -o %s' % util.shellquote(path)
            clientspec = marshal.load(util.popen(cmd, mode='rb'))

            views = {}
            for client in clientspec:
                if client.startswith("View"):
                    sview, cview = clientspec[client].split()
                    self._parse_view(sview)
                    if sview.endswith("...") and cview.endswith("..."):
                        sview = sview[:-3]
                        cview = cview[:-3]
                    cview = cview[2:]
                    cview = cview[cview.find("/") + 1:]
                    views[sview] = cview

        # list of changes that affect our source files
        self.p4changes = self.p4changes.keys()
        self.p4changes.sort(key=int)

        # list with depot pathnames, longest first
        vieworder = views.keys()
        vieworder.sort(key=len, reverse=True)

        # handle revision limiting
        startrev = self.ui.config('convert', 'p4.startrev', default=0)
        self.p4changes = [x for x in self.p4changes
                          if ((not startrev or int(x) >= int(startrev)) and
                              (not self.rev or int(x) <= int(self.rev)))]

        # now read the full changelists to get the list of file revisions
        ui.status(_('collecting p4 changelists\n'))
        lastid = None
        for change in self.p4changes:
            cmd = "p4 -G describe -s %s" % change
            stdout = util.popen(cmd, mode='rb')
            d = marshal.load(stdout)
            desc = self.recode(d["desc"])
            shortdesc = desc.split("\n", 1)[0]
            t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
            ui.status(util.ellipsis(t, 80) + '\n')

            if lastid:
                parents = [lastid]
            else:
                parents = []

            date = (int(d["time"]), 0)     # timezone not set
            c = commit(author=self.recode(d["user"]), date=util.datestr(date),
                       parents=parents, desc=desc, branch='',
                       extra={"p4": change})

            files = []
            i = 0
            while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
                oldname = d["depotFile%d" % i]
                filename = None
                for v in vieworder:
                    if oldname.startswith(v):
                        filename = views[v] + oldname[len(v):]
                        break
                if filename:
                    files.append((filename, d["rev%d" % i]))
                    self.depotname[filename] = oldname
                i += 1
            self.changeset[change] = c
            self.files[change] = files
            lastid = change

        if lastid:
            self.heads = [lastid]
Exemplo n.º 10
0
def createlog(ui, directory=None, root="", rlog=True, cache=None):
    '''Collect the CVS rlog'''

    # Because we store many duplicate commit log messages, reusing strings
    # saves a lot of memory and pickle storage space.
    _scache = {}

    def scache(s):
        "return a shared version of a string"
        return _scache.setdefault(s, s)

    ui.status(_('collecting CVS rlog\n'))

    log = []  # list of logentry objects containing the CVS state

    # patterns to match in CVS (r)log output, by state of use
    re_00 = re.compile('RCS file: (.+)$')
    re_01 = re.compile('cvs \\[r?log aborted\\]: (.+)$')
    re_02 = re.compile('cvs (r?log|server): (.+)\n$')
    re_03 = re.compile("(Cannot access.+CVSROOT)|"
                       "(can't create temporary directory.+)$")
    re_10 = re.compile('Working file: (.+)$')
    re_20 = re.compile('symbolic names:')
    re_30 = re.compile('\t(.+): ([\\d.]+)$')
    re_31 = re.compile('----------------------------$')
    re_32 = re.compile('======================================='
                       '======================================$')
    re_50 = re.compile('revision ([\\d.]+)(\s+locked by:\s+.+;)?$')
    re_60 = re.compile(r'date:\s+(.+);\s+author:\s+(.+);\s+state:\s+(.+?);'
                       r'(\s+lines:\s+(\+\d+)?\s+(-\d+)?;)?'
                       r'(.*mergepoint:\s+([^;]+);)?')
    re_70 = re.compile('branches: (.+);$')

    file_added_re = re.compile(r'file [^/]+ was (initially )?added on branch')

    prefix = ''  # leading path to strip of what we get from CVS

    if directory is None:
        # Current working directory

        # Get the real directory in the repository
        try:
            prefix = open(os.path.join('CVS', 'Repository')).read().strip()
            directory = prefix
            if prefix == ".":
                prefix = ""
        except IOError:
            raise logerror(_('not a CVS sandbox'))

        if prefix and not prefix.endswith(os.sep):
            prefix += os.sep

        # Use the Root file in the sandbox, if it exists
        try:
            root = open(os.path.join('CVS', 'Root')).read().strip()
        except IOError:
            pass

    if not root:
        root = os.environ.get('CVSROOT', '')

    # read log cache if one exists
    oldlog = []
    date = None

    if cache:
        cachedir = os.path.expanduser('~/.hg.cvsps')
        if not os.path.exists(cachedir):
            os.mkdir(cachedir)

        # The cvsps cache pickle needs a uniquified name, based on the
        # repository location. The address may have all sort of nasties
        # in it, slashes, colons and such. So here we take just the
        # alphanumerics, concatenated in a way that does not mix up the
        # various components, so that
        #    :pserver:user@server:/path
        # and
        #    /pserver/user/server/path
        # are mapped to different cache file names.
        cachefile = root.split(":") + [directory, "cache"]
        cachefile = ['-'.join(re.findall(r'\w+', s)) for s in cachefile if s]
        cachefile = os.path.join(cachedir,
                                 '.'.join([s for s in cachefile if s]))

    if cache == 'update':
        try:
            ui.note(_('reading cvs log cache %s\n') % cachefile)
            oldlog = pickle.load(open(cachefile))
            ui.note(_('cache has %d log entries\n') % len(oldlog))
        except Exception, e:
            ui.note(_('error reading cache: %r\n') % e)

        if oldlog:
            date = oldlog[-1].date  # last commit date as a (time,tz) tuple
            date = util.datestr(date, '%Y/%m/%d %H:%M:%S %1%2')
Exemplo n.º 11
0
            if cs.branch not in branches and cs.parents and cs.parents[0].id:
                ancestors[cs.branch] = (changesets[cs.parents[0].id -
                                                   1].branch, cs.parents[0].id)
            branches[cs.branch] = cs.id

        # limit by branches
        if opts["branches"] and (cs.branch or 'HEAD') not in opts["branches"]:
            continue

        if not off:
            # Note: trailing spaces on several lines here are needed to have
            #       bug-for-bug compatibility with cvsps.
            ui.write('---------------------\n')
            ui.write('PatchSet %d \n' % cs.id)
            ui.write('Date: %s\n' %
                     util.datestr(cs.date, '%Y/%m/%d %H:%M:%S %1%2'))
            ui.write('Author: %s\n' % cs.author)
            ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
            ui.write(
                'Tag%s: %s \n' %
                (['', 's'][len(cs.tags) > 1], ','.join(cs.tags) or '(none)'))
            branchpoints = getattr(cs, 'branchpoints', None)
            if branchpoints:
                ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
            if opts["parents"] and cs.parents:
                if len(cs.parents) > 1:
                    ui.write('Parents: %s\n' %
                             (','.join([str(p.id) for p in cs.parents])))
                else:
                    ui.write('Parent: %d\n' % cs.parents[0].id)
class notifier(object):
    '''email notification class.'''
    def __init__(self, ui, repo, hooktype):
        self.ui = ui
        cfg = self.ui.config('notify', 'config')
        if cfg:
            self.ui.readconfig(cfg, sections=['usersubs', 'reposubs'])
        self.repo = repo
        self.stripcount = int(self.ui.config('notify', 'strip', 0))
        self.root = self.strip(self.repo.root)
        self.domain = self.ui.config('notify', 'domain')
        self.test = self.ui.configbool('notify', 'test', True)
        self.charsets = mail._charsets(self.ui)
        self.subs = self.subscribers()
        self.merge = self.ui.configbool('notify', 'merge', True)

        mapfile = self.ui.config('notify', 'style')
        template = (self.ui.config('notify', hooktype)
                    or self.ui.config('notify', 'template'))
        self.t = cmdutil.changeset_templater(self.ui, self.repo, False, None,
                                             mapfile, False)
        if not mapfile and not template:
            template = deftemplates.get(hooktype) or single_template
        if template:
            template = templater.parsestring(template, quoted=False)
            self.t.use_template(template)

    def strip(self, path):
        '''strip leading slashes from local path, turn into web-safe path.'''

        path = util.pconvert(path)
        count = self.stripcount
        while count > 0:
            c = path.find('/')
            if c == -1:
                break
            path = path[c + 1:]
            count -= 1
        return path

    def fixmail(self, addr):
        '''try to clean up email addresses.'''

        addr = util.email(addr.strip())
        if self.domain:
            a = addr.find('@localhost')
            if a != -1:
                addr = addr[:a]
            if '@' not in addr:
                return addr + '@' + self.domain
        return addr

    def subscribers(self):
        '''return list of email addresses of subscribers to this repo.'''
        subs = set()
        for user, pats in self.ui.configitems('usersubs'):
            for pat in pats.split(','):
                if fnmatch.fnmatch(self.repo.root, pat.strip()):
                    subs.add(self.fixmail(user))
        for pat, users in self.ui.configitems('reposubs'):
            if fnmatch.fnmatch(self.repo.root, pat):
                for user in users.split(','):
                    subs.add(self.fixmail(user))
        return [
            mail.addressencode(self.ui, s, self.charsets, self.test)
            for s in sorted(subs)
        ]

    def url(self, path=None):
        return self.ui.config('web', 'baseurl') + (path or self.root)

    def node(self, ctx, **props):
        '''format one changeset, unless it is a suppressed merge.'''
        if not self.merge and len(ctx.parents()) > 1:
            return False
        self.t.show(ctx,
                    changes=ctx.changeset(),
                    baseurl=self.ui.config('web', 'baseurl'),
                    root=self.repo.root,
                    webroot=self.root,
                    **props)
        return True

    def skipsource(self, source):
        '''true if incoming changes from this source should be skipped.'''
        ok_sources = self.ui.config('notify', 'sources', 'serve').split()
        return source not in ok_sources

    def send(self, ctx, count, data):
        '''send message.'''

        p = email.Parser.Parser()
        try:
            msg = p.parsestr(data)
        except email.Errors.MessageParseError, inst:
            raise util.Abort(inst)

        # store sender and subject
        sender, subject = msg['From'], msg['Subject']
        del msg['From'], msg['Subject']

        if not msg.is_multipart():
            # create fresh mime message from scratch
            # (multipart templates must take care of this themselves)
            headers = msg.items()
            payload = msg.get_payload()
            # for notification prefer readability over data precision
            msg = mail.mimeencode(self.ui, payload, self.charsets, self.test)
            # reinstate custom headers
            for k, v in headers:
                msg[k] = v

        msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")

        # try to make subject line exist and be useful
        if not subject:
            if count > 1:
                subject = _('%s: %d new changesets') % (self.root, count)
            else:
                s = ctx.description().lstrip().split('\n', 1)[0].rstrip()
                subject = '%s: %s' % (self.root, s)
        maxsubject = int(self.ui.config('notify', 'maxsubject', 67))
        if maxsubject:
            subject = util.ellipsis(subject, maxsubject)
        msg['Subject'] = mail.headencode(self.ui, subject, self.charsets,
                                         self.test)

        # try to make message have proper sender
        if not sender:
            sender = self.ui.config('email', 'from') or self.ui.username()
        if '@' not in sender or '@localhost' in sender:
            sender = self.fixmail(sender)
        msg['From'] = mail.addressencode(self.ui, sender, self.charsets,
                                         self.test)

        msg['X-Hg-Notification'] = 'changeset %s' % ctx
        if not msg['Message-Id']:
            msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (ctx, int(
                time.time()), hash(self.repo.root), socket.getfqdn()))
        msg['To'] = ', '.join(self.subs)

        msgtext = msg.as_string()
        if self.test:
            self.ui.write(msgtext)
            if not msgtext.endswith('\n'):
                self.ui.write('\n')
        else:
            self.ui.status(
                _('notify: sending %d subscribers %d changes\n') %
                (len(self.subs), count))
            mail.sendmail(self.ui, util.email(msg['From']), self.subs, msgtext)