source: rtems-tools/rtemstoolkit/git.py @ 04a5204

4.105
Last change on this file since 04a5204 was 04a5204, checked in by Sebastian Huber <sebastian.huber@…>, on 11/12/15 at 10:15:23

Python 3 compatibility

  • Property mode set to 100644
File size: 6.9 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2014 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31#
32# Provide some basic access to the git command.
33#
34
35import os
36
37import error
38import execute
39import log
40import options
41import path
42
43class repo:
44    """An object to manage a git repo."""
45
46    def _git_exit_code(self, ec):
47        if ec:
48            raise error.general('git command failed (%s): %d' % (self.git, ec))
49
50    def _run(self, args, check = False):
51        e = execute.capture_execution()
52        if path.exists(self.path):
53            cwd = self.path
54        else:
55            cwd = None
56        cmd = [self.git] + args
57        log.trace('cmd: (%s) %s' % (str(cwd), ' '.join(cmd)))
58        exit_code, proc, output = e.spawn(cmd, cwd = path.host(cwd))
59        log.trace(output)
60        if check:
61            self._git_exit_code(exit_code)
62        return exit_code, output
63
64    def __init__(self, _path, opts, macros = None):
65        self.path = _path
66        self.opts = opts
67        if macros is None:
68            self.macros = opts.defaults
69        else:
70            self.macros = macros
71        self.git = self.macros.expand('%{__git}')
72
73    def git_version(self):
74        ec, output = self._run(['--version'], True)
75        gvs = output.split()
76        if len(gvs) < 3:
77            raise error.general('invalid version string from git: %s' % (output))
78        vs = gvs[2].split('.')
79        if len(vs) != 4:
80            raise error.general('invalid version number from git: %s' % (gvs[2]))
81        return (int(vs[0]), int(vs[1]), int(vs[2]), int(vs[3]))
82
83    def clone(self, url, _path):
84        ec, output = self._run(['clone', url, path.host(_path)], check = True)
85
86    def fetch(self):
87        ec, output = self._run(['fetch'], check = True)
88
89    def pull(self):
90        ec, output = self._run(['pull'], check = True)
91
92    def reset(self, args):
93        if type(args) == str:
94            args = [args]
95        ec, output = self._run(['reset'] + args, check = True)
96
97    def branch(self):
98        ec, output = self._run(['branch'])
99        if ec == 0:
100            for b in output.split('\n'):
101                if b[0] == '*':
102                    return b[2:]
103        return None
104
105    def checkout(self, branch = 'master'):
106        ec, output = self._run(['checkout', branch], check = True)
107
108    def status(self):
109        _status = {}
110        if path.exists(self.path):
111            ec, output = self._run(['status'])
112            if ec == 0:
113                state = 'none'
114                for l in output.split('\n'):
115                    if l.startswith('# On branch '):
116                        _status['branch'] = l[len('# On branch '):]
117                    elif l.startswith('# Changes to be committed:'):
118                        state = 'staged'
119                    elif l.startswith('# Changes not staged for commit:'):
120                        state = 'unstaged'
121                    elif l.startswith('# Untracked files:'):
122                        state = 'untracked'
123                    elif state != 'none' and l[0] == '#':
124                        if l.strip() != '#' and not l.startswith('#   ('):
125                            if state not in _status:
126                                _status[state] = []
127                            l = l[1:]
128                            if ':' in l:
129                                l = l.split(':')[1]
130                            _status[state] += [l.strip()]
131        return _status
132
133    def clean(self):
134        _status = self.status()
135        return len(_status) == 1 and 'branch' in _status
136
137    def valid(self):
138        if path.exists(self.path):
139            ec, output = self._run(['status'])
140            return ec == 0
141        return False
142
143    def remotes(self):
144        _remotes = {}
145        ec, output = self._run(['config', '--list'])
146        if ec == 0:
147            for l in output.split('\n'):
148                if l.startswith('remote'):
149                    ls = l.split('=')
150                    if len(ls) >= 2:
151                        rs = ls[0].split('.')
152                        if len(rs) == 3:
153                            r_name = rs[1]
154                            r_type = rs[2]
155                            if r_name not in _remotes:
156                                _remotes[r_name] = {}
157                            if r_type not in _remotes[r_name]:
158                                _remotes[r_name][r_type] = []
159                            _remotes[r_name][r_type] = '='.join(ls[1:])
160        return _remotes
161
162    def email(self):
163        _email = None
164        _name = None
165        ec, output = self._run(['config', '--list'])
166        if ec == 0:
167            for l in output.split('\n'):
168                if l.startswith('user.email'):
169                    ls = l.split('=')
170                    if len(ls) >= 2:
171                        _email = ls[1]
172                elif l.startswith('user.name'):
173                    ls = l.split('=')
174                    if len(ls) >= 2:
175                        _name = ls[1]
176        if _email is not None:
177            if _name is not None:
178                _email = '%s <%s>' % (_name, _email)
179            return _email
180        return None
181
182    def head(self):
183        hash = ''
184        ec, output = self._run(['log', '-n', '1'])
185        if ec == 0:
186            l1 = output.split('\n')[0]
187            if l1.startswith('commit '):
188                hash = l1[len('commit '):]
189        return hash
190
191if __name__ == '__main__':
192    import sys
193    opts = options.load(sys.argv)
194    g = repo('.', opts)
195    print(g.git_version())
196    print(g.valid())
197    print(g.status())
198    print(g.clean())
199    print(g.remotes())
200    print(g.email())
201    print(g.head())
Note: See TracBrowser for help on using the repository browser.