Project

General

Profile

Defect #37659 » redminehelper.py

seems to work with pythom 3.9 - Norbert Koch, 2022-09-10 21:08

 
1
# redminehelper: Redmine helper extension for Mercurial
2
#
3
# Copyright 2010 Alessio Franceschelli (alefranz.net)
4
# Copyright 2010-2011 Yuya Nishihara <yuya@tcha.org>
5
#
6
# This software may be used and distributed according to the terms of the
7
# GNU General Public License version 2 or any later version.
8
"""helper commands for Redmine to reduce the number of hg calls
9

    
10
To test this extension, please try::
11

    
12
    $ hg --config extensions.redminehelper=redminehelper.py rhsummary
13

    
14
I/O encoding:
15

    
16
:file path: urlencoded, raw string
17
:tag name: utf-8
18
:branch name: utf-8
19
:node: hex string
20

    
21
Output example of rhsummary::
22

    
23
    <?xml version="1.0"?>
24
    <rhsummary>
25
      <repository root="/foo/bar">
26
        <tip revision="1234" node="abcdef0123..."/>
27
        <tag revision="123" node="34567abc..." name="1.1.1"/>
28
        <branch .../>
29
        ...
30
      </repository>
31
    </rhsummary>
32

    
33
Output example of rhmanifest::
34

    
35
    <?xml version="1.0"?>
36
    <rhmanifest>
37
      <repository root="/foo/bar">
38
        <manifest revision="1234" path="lib">
39
          <file name="diff.rb" revision="123" node="34567abc..." time="12345"
40
                 size="100"/>
41
          ...
42
          <dir name="redmine"/>
43
          ...
44
        </manifest>
45
      </repository>
46
    </rhmanifest>
47
"""
48
import re, time, html, urllib
49
from mercurial import cmdutil, commands, node, error, hg, registrar
50

    
51
cmdtable = {}
52
command = registrar.command(cmdtable) if hasattr(registrar, 'command') else cmdutil.command(cmdtable)
53

    
54
_x = lambda s: html.escape(s.decode('utf-8')).encode('utf-8')
55
_u = lambda s: html.escape(urllib.parse.quote(s)).encode('utf-8')
56

    
57
def unquoteplus(*args, **kwargs):
58
    return urllib.parse.unquote_to_bytes(*args, **kwargs).replace(b'+', b' ')
59

    
60
def _changectx(repo, rev):
61
    if isinstance(rev, bytes):
62
       rev = repo.lookup(rev)
63
    if hasattr(repo, 'changectx'):
64
        return repo.changectx(rev)
65
    else:
66
        return repo[rev]
67

    
68
def _tip(ui, repo):
69
    # see mercurial/commands.py:tip
70
    def tiprev():
71
        try:
72
            return len(repo) - 1
73
        except TypeError:  # Mercurial < 1.1
74
            return repo.changelog.count() - 1
75
    tipctx = _changectx(repo, tiprev())
76
    ui.write(b'<tip revision="%d" node="%s"/>\n'
77
             % (tipctx.rev(), _x(node.hex(tipctx.node()))))
78

    
79
_SPECIAL_TAGS = (b'tip',)
80

    
81
def _tags(ui, repo):
82
    # see mercurial/commands.py:tags
83
    for t, n in reversed(repo.tagslist()):
84
        if t in _SPECIAL_TAGS:
85
            continue
86
        try:
87
            r = repo.changelog.rev(n)
88
        except error.LookupError:
89
            continue
90
        ui.write(b'<tag revision="%d" node="%s" name="%s"/>\n'
91
                 % (r, _x(node.hex(n)), _u(t)))
92

    
93
def _branches(ui, repo):
94
    # see mercurial/commands.py:branches
95
    def iterbranches():
96
        if getattr(repo, 'branchtags', None) is not None:
97
            # Mercurial < 2.9
98
            for t, n in repo.branchtags().iteritems():
99
                yield t, n, repo.changelog.rev(n)
100
        else:
101
            for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
102
                yield tag, tip, repo.changelog.rev(tip)
103
    def branchheads(branch):
104
        try:
105
            return repo.branchheads(branch, closed=False)
106
        except TypeError:  # Mercurial < 1.2
107
            return repo.branchheads(branch)
108
    def lookup(rev, n):
109
        try:
110
            return repo.lookup(rev)
111
        except RuntimeError:
112
            return n
113
    for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True):
114
        if lookup(r, n) in branchheads(t):
115
            ui.write(b'<branch revision="%d" node="%s" name="%s"/>\n'
116
                     % (r, _x(node.hex(n)), _u(t)))
117

    
118
def _manifest(ui, repo, path, rev):
119
    ctx = _changectx(repo, rev)
120
    ui.write(b'<manifest revision="%d" path="%s">\n'
121
             % (ctx.rev(), _u(path)))
122

    
123
    known = set()
124
    pathprefix = (path.decode('utf-8').rstrip('/') + '/').lstrip('/')
125
    for f, n in sorted(ctx.manifest().iteritems(), key=lambda e: e[0]):
126
        fstr = f.decode('utf-8')
127
        if not fstr.startswith(pathprefix):
128
            continue
129
        name = re.sub(r'/.*', '/', fstr[len(pathprefix):])
130
        if name in known:
131
            continue
132
        known.add(name)
133

    
134
        if name.endswith('/'):
135
            ui.write(b'<dir name="%s"/>\n'
136
                     % _x(urllib.parse.quote(name[:-1]).encode('utf-8')))
137
        else:
138
            fctx = repo.filectx(f, fileid=n)
139
            tm, tzoffset = fctx.date()
140
            ui.write(b'<file name="%s" revision="%d" node="%s" '
141
                     b'time="%d" size="%d"/>\n'
142
                     % (_u(name), fctx.rev(), _x(node.hex(fctx.node())),
143
                        tm, fctx.size(), ))
144

    
145
    ui.write(b'</manifest>\n')
146

    
147
@command(b'rhannotate',
148
         [(b'r', b'rev', b'', b'revision'),
149
          (b'u', b'user', None, b'list the author (long with -v)'),
150
          (b'n', b'number', None, b'list the revision number (default)'),
151
          (b'c', b'changeset', None, b'list the changeset'),
152
         ],
153
         b'hg rhannotate [-r REV] [-u] [-n] [-c] FILE...')
154
def rhannotate(ui, repo, *pats, **opts):
155
    rev = unquoteplus(opts.pop('rev', b''))
156
    opts['rev'] = rev
157
    return commands.annotate(ui, repo, *map(unquoteplus, pats), **opts)
158

    
159
@command(b'rhcat',
160
               [(b'r', b'rev', b'', b'revision')],
161
               b'hg rhcat ([-r REV] ...) FILE...')
162
def rhcat(ui, repo, file1, *pats, **opts):
163
    rev = unquoteplus(opts.pop(b'rev', b''))
164
    opts['rev'] = rev
165
    return commands.cat(ui, repo, unquote_plus(file1), *map(unquoteplus, pats), **opts)
166

    
167
@command(b'rhdiff',
168
               [(b'r', b'rev', [], b'revision'),
169
                (b'c', b'change', b'', b'change made by revision')],
170
               b'hg rhdiff ([-c REV] | [-r REV] ...) [FILE]...')
171
def rhdiff(ui, repo, *pats, **opts):
172
    """diff repository (or selected files)"""
173
    change = opts.pop('change', None)
174
    if change:  # add -c option for Mercurial<1.1
175
        base = _changectx(repo, change).parents()[0].rev()
176
        opts['rev'] = [base, change]
177
    opts['nodates'] = True
178
    return commands.diff(ui, repo, *map(unquoteplus, pats), **opts)
179

    
180
@command(b'rhlog',
181
                   [
182
                    (b'r', b'rev', [], b'show the specified revision'),
183
                    (b'b', b'branch', [],
184
                       b'show changesets within the given named branch'),
185
                    (b'l', b'limit', b'',
186
                         b'limit number of changes displayed'),
187
                    (b'd', b'date', b'',
188
                         b'show revisions matching date spec'),
189
                    (b'u', b'user', [],
190
                      b'revisions committed by user'),
191
                    (b'', b'from', b'',
192
                      b''),
193
                    (b'', b'to', b'',
194
                      b''),
195
                    (b'', b'rhbranch', b'',
196
                      b''),
197
                    (b'', b'template', b'',
198
                       b'display with template')],
199
                   b'hg rhlog [OPTION]... [FILE]')
200
def rhlog(ui, repo, *pats, **opts):
201
    rev      = opts.pop('rev')
202
    bra0     = opts.pop('branch')
203
    from_rev = unquoteplus(opts.pop('from', b''))
204
    to_rev   = unquoteplus(opts.pop('to'  , b''))
205
    bra      = urllib.unquote_plus(opts.pop('rhbranch', b''))
206
    from_rev = from_rev.replace(b'"', b'\\"')
207
    to_rev   = to_rev.replaceb('"', b'\\"')
208
    if (from_rev != b'') or (to_rev != b''):
209
        if from_rev != b'':
210
            quotefrom = b'"%s"' % (from_rev)
211
        else:
212
            quotefrom = from_rev
213
        if to_rev != b'':
214
            quoteto = b'"%s"' % (to_rev)
215
        else:
216
            quoteto = to_rev
217
        opts['rev'] = [b'%s:%s' % (quotefrom, quoteto)]
218
    else:
219
        opts['rev'] = rev
220
    if (bra != b''):
221
        opts['branch'] = [bra]
222
    return commands.log(ui, repo, *map(unquoteplus, pats), **opts)
223

    
224
@command(b'rhmanifest',
225
                   [(b'r', b'rev', b'', b'show the specified revision')],
226
                   b'hg rhmanifest [-r REV] [PATH]')
227
def rhmanifest(ui, repo, path=b'', **opts):
228
    """output the sub-manifest of the specified directory"""
229
    ui.write(b'<?xml version="1.0"?>\n')
230
    ui.write(b'<rhmanifest>\n')
231
    ui.write(b'<repository root="%s">\n' % _u(repo.root))
232
    try:
233
        _manifest(ui, repo, unquoteplus(path), unquoteplus(opts.get('rev')))
234
    finally:
235
        ui.write(b'</repository>\n')
236
        ui.write(b'</rhmanifest>\n')
237

    
238
@command(b'rhsummary',[], b'hg rhsummary')
239
def rhsummary(ui, repo, **opts):
240
    """output the summary of the repository"""
241
    ui.write(b'<?xml version="1.0"?>\n')
242
    ui.write(b'<rhsummary>\n')
243
    ui.write(b'<repository root="%s">\n' % _u(repo.root))
244
    try:
245
        _tip(ui, repo)
246
        _tags(ui, repo)
247
        _branches(ui, repo)
248
        # TODO: bookmarks in core (Mercurial>=1.8)
249
    finally:
250
        ui.write(b'</repository>\n')
251
        ui.write(b'</rhsummary>\n')
252

    
(1-1/4)