summaryrefslogtreecommitdiff
path: root/rtemstoolkit/git.py
diff options
context:
space:
mode:
Diffstat (limited to 'rtemstoolkit/git.py')
-rw-r--r--rtemstoolkit/git.py201
1 files changed, 201 insertions, 0 deletions
diff --git a/rtemstoolkit/git.py b/rtemstoolkit/git.py
new file mode 100644
index 0000000..2c23c05
--- /dev/null
+++ b/rtemstoolkit/git.py
@@ -0,0 +1,201 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2010-2014 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Provide some basic access to the git command.
+#
+
+import os
+
+import error
+import execute
+import log
+import options
+import path
+
+class repo:
+ """An object to manage a git repo."""
+
+ def _git_exit_code(self, ec):
+ if ec:
+ raise error.general('git command failed (%s): %d' % (self.git, ec))
+
+ def _run(self, args, check = False):
+ e = execute.capture_execution()
+ if path.exists(self.path):
+ cwd = self.path
+ else:
+ cwd = None
+ cmd = [self.git] + args
+ log.trace('cmd: (%s) %s' % (str(cwd), ' '.join(cmd)))
+ exit_code, proc, output = e.spawn(cmd, cwd = path.host(cwd))
+ log.trace(output)
+ if check:
+ self._git_exit_code(exit_code)
+ return exit_code, output
+
+ def __init__(self, _path, opts, macros = None):
+ self.path = _path
+ self.opts = opts
+ if macros is None:
+ self.macros = opts.defaults
+ else:
+ self.macros = macros
+ self.git = self.macros.expand('%{__git}')
+
+ def git_version(self):
+ ec, output = self._run(['--version'], True)
+ gvs = output.split()
+ if len(gvs) < 3:
+ raise error.general('invalid version string from git: %s' % (output))
+ vs = gvs[2].split('.')
+ if len(vs) != 4:
+ raise error.general('invalid version number from git: %s' % (gvs[2]))
+ return (int(vs[0]), int(vs[1]), int(vs[2]), int(vs[3]))
+
+ def clone(self, url, _path):
+ ec, output = self._run(['clone', url, path.host(_path)], check = True)
+
+ def fetch(self):
+ ec, output = self._run(['fetch'], check = True)
+
+ def pull(self):
+ ec, output = self._run(['pull'], check = True)
+
+ def reset(self, args):
+ if type(args) == str:
+ args = [args]
+ ec, output = self._run(['reset'] + args, check = True)
+
+ def branch(self):
+ ec, output = self._run(['branch'])
+ if ec == 0:
+ for b in output.split('\n'):
+ if b[0] == '*':
+ return b[2:]
+ return None
+
+ def checkout(self, branch = 'master'):
+ ec, output = self._run(['checkout', branch], check = True)
+
+ def status(self):
+ _status = {}
+ if path.exists(self.path):
+ ec, output = self._run(['status'])
+ if ec == 0:
+ state = 'none'
+ for l in output.split('\n'):
+ if l.startswith('# On branch '):
+ _status['branch'] = l[len('# On branch '):]
+ elif l.startswith('# Changes to be committed:'):
+ state = 'staged'
+ elif l.startswith('# Changes not staged for commit:'):
+ state = 'unstaged'
+ elif l.startswith('# Untracked files:'):
+ state = 'untracked'
+ elif state != 'none' and l[0] == '#':
+ if l.strip() != '#' and not l.startswith('# ('):
+ if state not in _status:
+ _status[state] = []
+ l = l[1:]
+ if ':' in l:
+ l = l.split(':')[1]
+ _status[state] += [l.strip()]
+ return _status
+
+ def clean(self):
+ _status = self.status()
+ return len(_status) == 1 and 'branch' in _status
+
+ def valid(self):
+ if path.exists(self.path):
+ ec, output = self._run(['status'])
+ return ec == 0
+ return False
+
+ def remotes(self):
+ _remotes = {}
+ ec, output = self._run(['config', '--list'])
+ if ec == 0:
+ for l in output.split('\n'):
+ if l.startswith('remote'):
+ ls = l.split('=')
+ if len(ls) >= 2:
+ rs = ls[0].split('.')
+ if len(rs) == 3:
+ r_name = rs[1]
+ r_type = rs[2]
+ if r_name not in _remotes:
+ _remotes[r_name] = {}
+ if r_type not in _remotes[r_name]:
+ _remotes[r_name][r_type] = []
+ _remotes[r_name][r_type] = '='.join(ls[1:])
+ return _remotes
+
+ def email(self):
+ _email = None
+ _name = None
+ ec, output = self._run(['config', '--list'])
+ if ec == 0:
+ for l in output.split('\n'):
+ if l.startswith('user.email'):
+ ls = l.split('=')
+ if len(ls) >= 2:
+ _email = ls[1]
+ elif l.startswith('user.name'):
+ ls = l.split('=')
+ if len(ls) >= 2:
+ _name = ls[1]
+ if _email is not None:
+ if _name is not None:
+ _email = '%s <%s>' % (_name, _email)
+ return _email
+ return None
+
+ def head(self):
+ hash = ''
+ ec, output = self._run(['log', '-n', '1'])
+ if ec == 0:
+ l1 = output.split('\n')[0]
+ if l1.startswith('commit '):
+ hash = l1[len('commit '):]
+ return hash
+
+if __name__ == '__main__':
+ import sys
+ opts = options.load(sys.argv)
+ g = repo('.', opts)
+ print g.git_version()
+ print g.valid()
+ print g.status()
+ print g.clean()
+ print g.remotes()
+ print g.email()
+ print g.head()