예제 #1
0
    def test_node_get_history_with_empty_commit(self):
        # regression test for #11328
        path = os.path.join(self.repos_path, '.git')
        DbRepositoryProvider(self.env).add_repository('gitrepos', path, 'git')
        repos = RepositoryManager(self.env).get_repository('gitrepos')
        parent_rev = repos.youngest_rev

        self._git_commit('-m', 'ticket:11328', '--allow-empty',
                         date=datetime(2013, 10, 15, 9, 46, 27))
        repos.sync()
        rev = repos.youngest_rev

        node = repos.get_node('', rev)
        self.assertEqual(rev, repos.git.last_change(rev, ''))
        history = list(node.get_history())
        self.assertEqual(u'', history[0][0])
        self.assertEqual(rev, history[0][1])
        self.assertEqual(Changeset.EDIT, history[0][2])
        self.assertEqual(u'', history[1][0])
        self.assertEqual(parent_rev, history[1][1])
        self.assertEqual(Changeset.ADD, history[1][2])
        self.assertEqual(2, len(history))
예제 #2
0
 def render_property(self, name, mode, context, props):
     """Parse svn:mergeinfo and svnmerge-* properties, converting branch
     names to links and providing links to the revision log for merged
     and eligible revisions.
     """
     has_eligible = name in ('svnmerge-integrated', 'svn:mergeinfo')
     revs_label = (_('merged'), _('blocked'))[name.endswith('blocked')]
     revs_cols = has_eligible and 2 or None
     reponame = context.resource.parent.id
     target_path = context.resource.id
     repos = RepositoryManager(self.env).get_repository(reponame)
     target_rev = context.resource.version
     if has_eligible:
         node = repos.get_node(target_path, target_rev)
         branch_starts = {}
         for path, rev in node.get_copy_ancestry(): 
             if path not in branch_starts:
                 branch_starts[path] = rev + 1
     rows = []
     if name.startswith('svnmerge-'):
         sources = props[name].split()
     else:
         sources = props[name].splitlines()
     for line in sources:
         path, revs = line.split(':', 1)
         spath = _path_within_scope(repos.scope, path)
         if spath is None:
             continue
         revs = revs.strip()
         inheritable, non_inheritable = _partition_inheritable(revs)
         revs = ','.join(inheritable)
         deleted = False
         try:
             node = repos.get_node(spath, target_rev)
             resource = context.resource.parent.child('source', spath)
             if 'LOG_VIEW' in context.perm(resource):
                 row = [_get_source_link(spath, context),
                        _get_revs_link(revs_label, context, spath, revs)]
                 if non_inheritable:
                     non_inheritable = ','.join(non_inheritable)
                     row.append(_get_revs_link(_('non-inheritable'), context,
                                               spath, non_inheritable,
                                               _('merged on the directory '
                                                 'itself but not below')))
                 if has_eligible:
                     first_rev = branch_starts.get(spath)
                     if not first_rev:
                         first_rev = node.get_branch_origin()
                     eligible = set(xrange(first_rev or 1, target_rev + 1))
                     eligible -= set(Ranges(revs))
                     blocked = _get_blocked_revs(props, name, spath)
                     if blocked:
                         eligible -= set(Ranges(blocked))
                     if eligible:
                         nrevs = repos._get_node_revs(spath, max(eligible),
                                                      min(eligible))
                         eligible &= set(nrevs)
                     eligible = to_ranges(eligible)
                     row.append(_get_revs_link(_('eligible'), context,
                                               spath, eligible))
                 rows.append((False, spath, [tag.td(each) for each in row]))
                 continue
         except NoSuchNode:
             deleted = True
         revs = revs.replace(',', u',\u200b')
         rows.append((deleted, spath,
                      [tag.td('/' + spath),
                       tag.td(revs, colspan=revs_cols)]))
     if not rows:
         return None
     rows.sort()
     has_deleted = rows and rows[-1][0] or None
     return tag(has_deleted and tag.a(_('(toggle deleted branches)'),
                                      class_='trac-toggledeleted',
                                      href='#'),
                tag.table(tag.tbody(
                    [tag.tr(row, class_=deleted and 'trac-deleted' or None)
                     for deleted, spath, row in rows]), class_='props'))
예제 #3
0
    def render_property(self, name, mode, context, props):
        """Parse svn:mergeinfo and svnmerge-* properties, converting branch
        names to links and providing links to the revision log for merged
        and eligible revisions.
        """
        has_eligible = name in ('svnmerge-integrated', 'svn:mergeinfo')
        revs_label = _('blocked') if name.endswith('blocked') else _('merged')
        revs_cols = 2 if has_eligible else None
        reponame = context.resource.parent.id
        target_path = context.resource.id
        repos = RepositoryManager(self.env).get_repository(reponame)
        target_rev = context.resource.version
        if has_eligible:
            node = repos.get_node(target_path, target_rev)
            branch_starts = {}
            for path, rev in node.get_copy_ancestry():
                if path not in branch_starts:
                    branch_starts[path] = rev + 1
        rows = []
        eligible_infos = []
        if name.startswith('svnmerge-'):
            sources = props[name].split()
        else:
            sources = props[name].splitlines()
        for line in sources:
            path, revs = line.split(':', 1)
            spath = _path_within_scope(repos.scope, path)
            if spath is None:
                continue
            revs = revs.strip()
            inheritable, non_inheritable = _partition_inheritable(revs)
            revs = ','.join(inheritable)
            deleted = False
            try:
                node = repos.get_node(spath, target_rev)
                resource = context.resource.parent.child('source', spath)
                if 'LOG_VIEW' in context.perm(resource):
                    row = [
                        _get_source_link(spath, context),
                        _get_revs_link(revs_label, context, spath, revs)
                    ]
                    if non_inheritable:
                        non_inheritable = ','.join(non_inheritable)
                        row.append(
                            _get_revs_link(
                                _('non-inheritable'), context, spath,
                                non_inheritable,
                                _('merged on the directory '
                                  'itself but not below')))
                    if has_eligible:
                        first_rev = branch_starts.get(spath)
                        if not first_rev:
                            first_rev = node.get_branch_origin()
                        eligible = set(xrange(first_rev or 1, target_rev + 1))
                        eligible -= set(Ranges(revs))
                        blocked = _get_blocked_revs(props, name, spath)
                        if blocked:
                            eligible -= set(Ranges(blocked))
                        if eligible:
                            node = repos.get_node(spath, max(eligible))
                            eligible_infos.append((spath, node, eligible, row))
                            continue
                        eligible = to_ranges(eligible)
                        row.append(
                            _get_revs_link(_('eligible'), context, spath,
                                           eligible))
                    rows.append((False, spath, [tag.td(each) for each in row]))
                    continue
            except NoSuchNode:
                deleted = True
            revs = revs.replace(',', u',\u200b')
            rows.append(
                (deleted, spath,
                 [tag.td('/' + spath),
                  tag.td(revs, colspan=revs_cols)]))

        # fetch eligible revisions for each path at a time
        changed_revs = {}
        changed_nodes = [(node, min(eligible))
                         for spath, node, eligible, row in eligible_infos]
        if changed_nodes:
            changed_revs = repos._get_changed_revs(changed_nodes)
        for spath, node, eligible, row in eligible_infos:
            if spath in changed_revs:
                eligible &= set(changed_revs[spath])
            else:
                eligible.clear()
            row.append(
                _get_revs_link(_("eligible"), context, spath,
                               to_ranges(eligible)))
            rows.append((False, spath, [tag.td(each) for each in row]))

        if not rows:
            return None
        rows.sort()
        if rows and rows[-1][0]:
            toggledeleted = tag.a(_("(toggle deleted branches)"),
                                  class_='trac-toggledeleted',
                                  href='#')
        else:
            toggledeleted = None
        return tag(
            toggledeleted,
            tag.table(tag.tbody([
                tag.tr(row, class_='trac-deleted' if deleted else None)
                for deleted, spath, row in rows
            ]),
                      class_='props'))
 def send_as_email(self, req, sender, recipients, subject, text, mode,  *resources):
     """
     `authname` Trac username of sender
     `sender` Tuple of (real name, email address)
     `recipients` List of (real name, email address) recipient address tuples
     `subject` The e-mail subject
     `text` The text body of the e-mail
     `files` List of paths to the files to send
     """
     assert len(resources) > 0, 'Nothing to send!'
     mailsys = self.distributor(self.env)
     from_addr = sender[1]
     root = MIMEMultipart('related')
     root.set_charset(self._make_charset())
     root.preamble = 'This is a multi-part message in MIME format.'
     headers = {}
     recp = [r[1] for r in recipients]
     headers['Subject'] = subject
     headers['To'] = ', '.join(recp)
     headers['From'] = from_addr
     headers['Date'] = formatdate()
     authname = req.authname
     files = []
     links = []
     attachments = []
     mimeview = Mimeview(self.env)
     for r in resources:
         repo = RepositoryManager(self.env).get_repository(r.parent.id)
         n = repo.get_node(r.id, rev=r.version)
         files.append(n.path)
         f = os.path.join(repo.repos.path, n.path)
         if mode in (self.LINKS_ONLY, self.LINKS_ATTACHMENTS):
             links.append((req.abs_href.browser(repo.reponame or None, n.path, format='raw'), os.path.basename(f)))
         if mode in (self.ATTACHMENTS_ONLY, self.LINKS_ATTACHMENTS):
             content = n.get_content().read()                
             mtype = n.get_content_type() or mimeview.get_mimetype(f, content)
             if not mtype:
                 mtype = 'application/octet-stream'
             if '; charset=' in mtype:
                 # What to use encoding for?
                 mtype, encoding = mtype.split('; charset=', 1)
             attachments.append(os.path.basename(f))
             maintype, subtype = mtype.split('/', 1)
             part = MIMEBase(maintype, subtype)
             part.set_payload(content)
             part.add_header('content-disposition', 'attachment', filename=os.path.basename(f))
             encode_base64(part)
             root.attach(part)
     body = self._format_email(authname, sender, recipients, subject, text, mode, links, attachments)
     msg = MIMEText(body, 'html', 'utf-8')
     root.attach(msg)
     del root['Content-Transfer-Encoding']
     for k, v in headers.items():
         set_header(root, k, v)
     email = (from_addr, recp, root.as_string())
     # Write mail to /tmp
     #import logging
     #if self.log.isEnabledFor(logging.DEBUG):
     #   (fd, tmpname) = mkstemp()
     #   os.write(fd, email[2])
     #   os.close(fd)
     #   self.log.debug('Wrote mail from %s to %s to %s', email[0], email[1], tmpname)
     self.log.info('Sending mail with items %s from %s to %s', resources, from_addr, recp)
     try:
         if using_announcer:
             if mailsys.use_threaded_delivery:
                 mailsys.get_delivery_queue().put(email)
             else:
                 mailsys.send(*email)
         else:
             mailsys.send_email(*email)
     except Exception, e:
         raise TracError(e.message)
     recipients.append(address)
 for f in files:
     # TODO: add ?rev=xxx and select correct version
     try:
         file_res = self._get_file_resource(req, realm='source',
                                            parent=repo.resource,
                                            path=f)
         # req.perm(file_res).require('BROWSER_VIEW') for the perm check maybe?
     except Exception, e:
         failures.append(str(e))
         continue
     if not hasattr(file_res, 'realm') or file_res.realm != 'source':
         failures.append("Resources must have the 'realm' attribute")
         continue
     repo = RepositoryManager(self.env).get_repository(file_res.parent.id)
     n = repo.get_node(file_res.id, rev=file_res.version)
     if not n.isfile:
         failures.append("Resources must be files")
         continue
     to_send.append(file_res)
 if not failures:
     try:
         files = self.send_as_email(req, sender, recipients, subject, message, mode, *to_send)
     except Exception, e:
         files = []
         failures.append(str(e))
 else:
     files = []
 response = dict(files=files, recipients=[x[1] for x in recipients],
                 failures=failures)
 if failures != []:
예제 #6
0
 def render_property(self, name, mode, context, props):
     """Parse svn:mergeinfo and svnmerge-* properties, converting branch
     names to links and providing links to the revision log for merged
     and eligible revisions.
     """
     has_eligible = name in ("svnmerge-integrated", "svn:mergeinfo")
     revs_label = _("blocked") if name.endswith("blocked") else _("merged")
     revs_cols = 2 if has_eligible else None
     reponame = context.resource.parent.id
     target_path = context.resource.id
     repos = RepositoryManager(self.env).get_repository(reponame)
     target_rev = context.resource.version
     if has_eligible:
         node = repos.get_node(target_path, target_rev)
         branch_starts = {}
         for path, rev in node.get_copy_ancestry():
             if path not in branch_starts:
                 branch_starts[path] = rev + 1
     rows = []
     if name.startswith("svnmerge-"):
         sources = props[name].split()
     else:
         sources = props[name].splitlines()
     for line in sources:
         path, revs = line.split(":", 1)
         spath = _path_within_scope(repos.scope, path)
         if spath is None:
             continue
         revs = revs.strip()
         inheritable, non_inheritable = _partition_inheritable(revs)
         revs = ",".join(inheritable)
         deleted = False
         try:
             node = repos.get_node(spath, target_rev)
             resource = context.resource.parent.child("source", spath)
             if "LOG_VIEW" in context.perm(resource):
                 row = [_get_source_link(spath, context), _get_revs_link(revs_label, context, spath, revs)]
                 if non_inheritable:
                     non_inheritable = ",".join(non_inheritable)
                     row.append(
                         _get_revs_link(
                             _("non-inheritable"),
                             context,
                             spath,
                             non_inheritable,
                             _("merged on the directory " "itself but not below"),
                         )
                     )
                 if has_eligible:
                     first_rev = branch_starts.get(spath)
                     if not first_rev:
                         first_rev = node.get_branch_origin()
                     eligible = set(xrange(first_rev or 1, target_rev + 1))
                     eligible -= set(Ranges(revs))
                     blocked = _get_blocked_revs(props, name, spath)
                     if blocked:
                         eligible -= set(Ranges(blocked))
                     if eligible:
                         nrevs = repos._get_node_revs(spath, max(eligible), min(eligible))
                         eligible &= set(nrevs)
                     eligible = to_ranges(eligible)
                     row.append(_get_revs_link(_("eligible"), context, spath, eligible))
                 rows.append((False, spath, [tag.td(each) for each in row]))
                 continue
         except NoSuchNode:
             deleted = True
         revs = revs.replace(",", u",\u200b")
         rows.append((deleted, spath, [tag.td("/" + spath), tag.td(revs, colspan=revs_cols)]))
     if not rows:
         return None
     rows.sort()
     has_deleted = rows[-1][0] if rows else None
     return tag(
         has_deleted and tag.a(_("(toggle deleted branches)"), class_="trac-toggledeleted", href="#"),
         tag.table(
             tag.tbody([tag.tr(row, class_="trac-deleted" if deleted else None) for deleted, spath, row in rows]),
             class_="props",
         ),
     )