[d7e4900] | 1 | # |
---|
| 2 | # RTEMS Tools Project (http://www.rtems.org/) |
---|
[3a972f6] | 3 | # Copyright 2010-2016 Chris Johns (chrisj@rtems.org) |
---|
[d7e4900] | 4 | # All rights reserved. |
---|
| 5 | # |
---|
| 6 | # This file is part of the RTEMS Tools package in 'rtems-tools'. |
---|
| 7 | # |
---|
| 8 | # Permission to use, copy, modify, and/or distribute this software for any |
---|
| 9 | # purpose with or without fee is hereby granted, provided that the above |
---|
| 10 | # copyright notice and this permission notice appear in all copies. |
---|
| 11 | # |
---|
| 12 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
---|
| 13 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
| 14 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
---|
| 15 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
| 16 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
---|
| 17 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
---|
| 18 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
| 19 | |
---|
| 20 | # |
---|
| 21 | # Provide some basic access to the git command. |
---|
| 22 | # |
---|
| 23 | |
---|
[3a972f6] | 24 | from __future__ import print_function |
---|
| 25 | |
---|
[d7e4900] | 26 | import os |
---|
| 27 | |
---|
| 28 | import error |
---|
| 29 | import execute |
---|
[5142bec] | 30 | import log |
---|
[cb12e48] | 31 | import options |
---|
[d7e4900] | 32 | import path |
---|
| 33 | |
---|
| 34 | class repo: |
---|
| 35 | """An object to manage a git repo.""" |
---|
| 36 | |
---|
| 37 | def _git_exit_code(self, ec): |
---|
| 38 | if ec: |
---|
| 39 | raise error.general('git command failed (%s): %d' % (self.git, ec)) |
---|
| 40 | |
---|
| 41 | def _run(self, args, check = False): |
---|
| 42 | e = execute.capture_execution() |
---|
[649a64c] | 43 | if path.exists(self.path): |
---|
| 44 | cwd = self.path |
---|
| 45 | else: |
---|
| 46 | cwd = None |
---|
[5142bec] | 47 | cmd = [self.git] + args |
---|
| 48 | log.trace('cmd: (%s) %s' % (str(cwd), ' '.join(cmd))) |
---|
[5237f1cc] | 49 | exit_code, proc, output = e.spawn(cmd, cwd = path.host(cwd)) |
---|
[5142bec] | 50 | log.trace(output) |
---|
[d7e4900] | 51 | if check: |
---|
| 52 | self._git_exit_code(exit_code) |
---|
| 53 | return exit_code, output |
---|
| 54 | |
---|
[74da24c] | 55 | def __init__(self, _path, opts = None, macros = None): |
---|
[d7e4900] | 56 | self.path = _path |
---|
[cb12e48] | 57 | self.opts = opts |
---|
[74da24c] | 58 | if macros is None and opts is not None: |
---|
[cb12e48] | 59 | self.macros = opts.defaults |
---|
| 60 | else: |
---|
| 61 | self.macros = macros |
---|
[d790668] | 62 | if self.macros is None: |
---|
| 63 | self.git = 'git' |
---|
| 64 | else: |
---|
| 65 | self.git = self.macros.expand('%{__git}') |
---|
[d7e4900] | 66 | |
---|
| 67 | def git_version(self): |
---|
| 68 | ec, output = self._run(['--version'], True) |
---|
| 69 | gvs = output.split() |
---|
| 70 | if len(gvs) < 3: |
---|
| 71 | raise error.general('invalid version string from git: %s' % (output)) |
---|
| 72 | vs = gvs[2].split('.') |
---|
| 73 | if len(vs) != 4: |
---|
| 74 | raise error.general('invalid version number from git: %s' % (gvs[2])) |
---|
| 75 | return (int(vs[0]), int(vs[1]), int(vs[2]), int(vs[3])) |
---|
| 76 | |
---|
[5237f1cc] | 77 | def clone(self, url, _path): |
---|
| 78 | ec, output = self._run(['clone', url, path.host(_path)], check = True) |
---|
[649a64c] | 79 | |
---|
[b0c2756] | 80 | def fetch(self): |
---|
| 81 | ec, output = self._run(['fetch'], check = True) |
---|
[649a64c] | 82 | |
---|
[b0f9e30] | 83 | def merge(self): |
---|
| 84 | ec, output = self._run(['merge'], check = True) |
---|
| 85 | |
---|
[649a64c] | 86 | def pull(self): |
---|
[b0c2756] | 87 | ec, output = self._run(['pull'], check = True) |
---|
[649a64c] | 88 | |
---|
| 89 | def reset(self, args): |
---|
| 90 | if type(args) == str: |
---|
| 91 | args = [args] |
---|
[b0c2756] | 92 | ec, output = self._run(['reset'] + args, check = True) |
---|
[649a64c] | 93 | |
---|
| 94 | def branch(self): |
---|
| 95 | ec, output = self._run(['branch']) |
---|
| 96 | if ec == 0: |
---|
| 97 | for b in output.split('\n'): |
---|
| 98 | if b[0] == '*': |
---|
| 99 | return b[2:] |
---|
| 100 | return None |
---|
| 101 | |
---|
| 102 | def checkout(self, branch = 'master'): |
---|
[b0c2756] | 103 | ec, output = self._run(['checkout', branch], check = True) |
---|
[649a64c] | 104 | |
---|
[5601b9c] | 105 | def submodule(self, module): |
---|
| 106 | ec, output = self._run(['submodule', 'update', '--init', module], check = True) |
---|
| 107 | |
---|
[c21f09e] | 108 | def clean(self, args = []): |
---|
[8c19df2] | 109 | if type(args) == str: |
---|
| 110 | args = [args] |
---|
| 111 | ec, output = self._run(['clean'] + args, check = True) |
---|
| 112 | |
---|
[d7e4900] | 113 | def status(self): |
---|
| 114 | _status = {} |
---|
[649a64c] | 115 | if path.exists(self.path): |
---|
| 116 | ec, output = self._run(['status']) |
---|
| 117 | if ec == 0: |
---|
| 118 | state = 'none' |
---|
| 119 | for l in output.split('\n'): |
---|
[c21f09e] | 120 | if l.startswith('# '): |
---|
| 121 | l = l[2:] |
---|
| 122 | if l.startswith('On branch '): |
---|
| 123 | _status['branch'] = l[len('On branch '):] |
---|
| 124 | elif l.startswith('Changes to be committed:'): |
---|
[649a64c] | 125 | state = 'staged' |
---|
[c21f09e] | 126 | elif l.startswith('Changes not staged for commit:'): |
---|
[649a64c] | 127 | state = 'unstaged' |
---|
[c21f09e] | 128 | elif l.startswith('Untracked files:'): |
---|
[649a64c] | 129 | state = 'untracked' |
---|
[c21f09e] | 130 | elif l.startswith('HEAD detached'): |
---|
[5601b9c] | 131 | state = 'detached' |
---|
[c21f09e] | 132 | elif state != 'none' and len(l.strip()) != 0: |
---|
| 133 | if l[0].isspace(): |
---|
| 134 | l = l.strip() |
---|
| 135 | if l[0] != '(': |
---|
| 136 | if state not in _status: |
---|
| 137 | _status[state] = [] |
---|
| 138 | l = l[1:] |
---|
| 139 | if ':' in l: |
---|
| 140 | l = l.split(':')[1] |
---|
| 141 | _status[state] += [l.strip()] |
---|
[d7e4900] | 142 | return _status |
---|
| 143 | |
---|
[8c19df2] | 144 | def dirty(self): |
---|
[d7e4900] | 145 | _status = self.status() |
---|
[c21f09e] | 146 | return not (len(_status) == 1 and 'branch' in _status) |
---|
[d7e4900] | 147 | |
---|
| 148 | def valid(self): |
---|
[649a64c] | 149 | if path.exists(self.path): |
---|
| 150 | ec, output = self._run(['status']) |
---|
| 151 | return ec == 0 |
---|
| 152 | return False |
---|
[d7e4900] | 153 | |
---|
| 154 | def remotes(self): |
---|
| 155 | _remotes = {} |
---|
| 156 | ec, output = self._run(['config', '--list']) |
---|
| 157 | if ec == 0: |
---|
| 158 | for l in output.split('\n'): |
---|
| 159 | if l.startswith('remote'): |
---|
| 160 | ls = l.split('=') |
---|
| 161 | if len(ls) >= 2: |
---|
| 162 | rs = ls[0].split('.') |
---|
| 163 | if len(rs) == 3: |
---|
| 164 | r_name = rs[1] |
---|
| 165 | r_type = rs[2] |
---|
| 166 | if r_name not in _remotes: |
---|
| 167 | _remotes[r_name] = {} |
---|
| 168 | if r_type not in _remotes[r_name]: |
---|
| 169 | _remotes[r_name][r_type] = [] |
---|
| 170 | _remotes[r_name][r_type] = '='.join(ls[1:]) |
---|
| 171 | return _remotes |
---|
| 172 | |
---|
[97a685f] | 173 | def email(self): |
---|
| 174 | _email = None |
---|
| 175 | _name = None |
---|
| 176 | ec, output = self._run(['config', '--list']) |
---|
| 177 | if ec == 0: |
---|
| 178 | for l in output.split('\n'): |
---|
| 179 | if l.startswith('user.email'): |
---|
| 180 | ls = l.split('=') |
---|
| 181 | if len(ls) >= 2: |
---|
| 182 | _email = ls[1] |
---|
| 183 | elif l.startswith('user.name'): |
---|
| 184 | ls = l.split('=') |
---|
| 185 | if len(ls) >= 2: |
---|
| 186 | _name = ls[1] |
---|
| 187 | if _email is not None: |
---|
| 188 | if _name is not None: |
---|
| 189 | _email = '%s <%s>' % (_name, _email) |
---|
| 190 | return _email |
---|
| 191 | return None |
---|
| 192 | |
---|
[d7e4900] | 193 | def head(self): |
---|
| 194 | hash = '' |
---|
| 195 | ec, output = self._run(['log', '-n', '1']) |
---|
| 196 | if ec == 0: |
---|
| 197 | l1 = output.split('\n')[0] |
---|
| 198 | if l1.startswith('commit '): |
---|
| 199 | hash = l1[len('commit '):] |
---|
| 200 | return hash |
---|
| 201 | |
---|
| 202 | if __name__ == '__main__': |
---|
| 203 | import sys |
---|
[cb12e48] | 204 | opts = options.load(sys.argv) |
---|
| 205 | g = repo('.', opts) |
---|
[3a972f6] | 206 | print(g.git_version()) |
---|
| 207 | print(g.valid()) |
---|
| 208 | print(g.status()) |
---|
| 209 | print(g.clean()) |
---|
| 210 | print(g.remotes()) |
---|
| 211 | print(g.email()) |
---|
| 212 | print(g.head()) |
---|