summaryrefslogtreecommitdiff
path: root/gr-utils/python/modtool/scm.py
diff options
context:
space:
mode:
Diffstat (limited to 'gr-utils/python/modtool/scm.py')
-rw-r--r--gr-utils/python/modtool/scm.py221
1 files changed, 221 insertions, 0 deletions
diff --git a/gr-utils/python/modtool/scm.py b/gr-utils/python/modtool/scm.py
new file mode 100644
index 0000000000..ec6023ab3b
--- /dev/null
+++ b/gr-utils/python/modtool/scm.py
@@ -0,0 +1,221 @@
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+""" Class to handle source code management repositories. """
+
+import subprocess
+
+try:
+ import git
+ HAS_GITPYTHON = True
+except ImportError:
+ HAS_GITPYTHON = False
+# GitPython is a bit too unstable currently
+HAS_GITPYTHON = False
+
+class InvalidSCMError(Exception):
+ """ Exception for when trying to access a repo of wrong type. """
+ def __init__(self):
+ Exception.__init__(self)
+
+### Base class ###############################################################
+class SCMRepository(object):
+ """ Base class to handle interactions with source code management systems. """
+ handles_scm_type = '*'
+ def __init__(self, path_to_repo, is_empty=False):
+ self.path_to_repo = path_to_repo
+ self.is_empty = is_empty
+
+ def init_repo(self, path_to_repo=None, add_files=True):
+ """ Initialize the directory as a repository. Assumes the self.path_to_repo
+ (or path_to_repo, if specified) does *not* contain a valid repository.
+ If add_files is True, all files in this directory are added to version control.
+ Returns true if actually created a repo.
+ """
+ if path_to_repo is not None:
+ self.path_to_repo = path_to_repo
+ return False
+
+ def add_files(self, paths_to_files):
+ """ Add a tuple or list of files to the current repository. """
+ pass
+
+ def add_file(self, path_to_file):
+ """ Add a file to the current repository. """
+ self.add_files([path_to_file])
+
+ def remove_files(self, paths_to_files):
+ """ Remove a tuple or list of files from the current repository. """
+ pass
+
+ def remove_file(self, path_to_file):
+ """ Remove a file from the current repository. """
+ self.remove_files([path_to_file])
+
+ def mark_files_updated(self, paths_to_files):
+ """ Mark a list of tuple of files as changed. """
+ pass
+
+ def mark_file_updated(self, path_to_file):
+ """ Mark a file as changed. """
+ self.mark_files_updated([path_to_file])
+
+ def is_active(self):
+ """ Returns true if this repository manager is operating on an active, source-controlled directory. """
+ return self.is_empty
+
+
+### Git #####################################################################
+class GitManagerGitPython(object):
+ """ Manage git through GitPython (preferred way). """
+ def __init__(self, path_to_repo, init=False):
+ if init:
+ self.repo = git.Repo.init(path_to_repo, mkdir=False)
+ else:
+ try:
+ self.repo = git.Repo(path_to_repo)
+ except git.InvalidGitRepositoryError:
+ self.repo = None
+ raise InvalidSCMError
+ self.index = self.repo.index
+
+ def add_files(self, paths_to_files):
+ """ Adds a tuple of files to the index of the current repository. """
+ if self.repo is not None:
+ self.index.add(paths_to_files)
+
+ def remove_files(self, paths_to_files):
+ """ Removes a tuple of files from the index of the current repository. """
+ if self.repo is not None:
+ self.index.remove(paths_to_files)
+
+
+class GitManagerShell(object):
+ """ Call the git executable through a shell. """
+ def __init__(self, path_to_repo, init=False, git_executable=None):
+ self.path_to_repo = path_to_repo
+ if git_executable is None:
+ try:
+ self.git_executable = subprocess.check_output('which git', shell=True).strip()
+ except (OSError, subprocess.CalledProcessError):
+ raise InvalidSCMError
+ try:
+ if init:
+ subprocess.check_output([self.git_executable, 'init'])
+ else:
+ subprocess.check_output([self.git_executable, 'status'])
+ except OSError:
+ raise InvalidSCMError
+ except subprocess.CalledProcessError:
+ raise InvalidSCMError
+
+ def add_files(self, paths_to_files):
+ """ Adds a tuple of files to the index of the current repository. Does not commit. """
+ subprocess.check_output([self.git_executable, 'add'] + list(paths_to_files))
+
+ def remove_files(self, paths_to_files):
+ """ Removes a tuple of files from the index of the current repository. Does not commit. """
+ subprocess.check_output([self.git_executable, 'rm', '--cached'] + list(paths_to_files))
+
+
+class GitRepository(SCMRepository):
+ """ Specific to operating on git repositories. """
+ handles_scm_type = 'git'
+ def __init__(self, path_to_repo, is_empty=False):
+ SCMRepository.__init__(self, path_to_repo, is_empty)
+ if not is_empty:
+ try:
+ if HAS_GITPYTHON:
+ self.repo_manager = GitManagerGitPython(path_to_repo)
+ else:
+ self.repo_manager = GitManagerShell(path_to_repo)
+ except InvalidSCMError:
+ self.repo_manager = None
+ else:
+ self.repo_manager = None
+
+ def init_repo(self, path_to_repo=None, add_files=True):
+ """ Makes the directory in self.path_to_repo a git repo.
+ If add_file is True, all files in this dir are added to the index. """
+ SCMRepository.init_repo(self, path_to_repo, add_files)
+ if HAS_GITPYTHON:
+ self.repo_manager = GitManagerGitPython(self.path_to_repo, init=True)
+ else:
+ self.repo_manager = GitManagerShell(self.path_to_repo, init=True)
+ if add_files:
+ self.add_files(('*',))
+ return True
+
+ def add_files(self, paths_to_files):
+ """ Add a file to the current repository. Does not commit. """
+ self.repo_manager.add_files(paths_to_files)
+
+ def remove_files(self, paths_to_files):
+ """ Remove a file from the current repository. Does not commit. """
+ self.repo_manager.remove_files(paths_to_files)
+
+ def mark_files_updated(self, paths_to_files):
+ """ Mark a file as changed. Since this is git, same as adding new files. """
+ self.add_files(paths_to_files)
+
+ def is_active(self):
+ return self.repo_manager is not None
+
+
+##############################################################################
+### Factory ##################################################################
+class SCMRepoFactory(object):
+ """ Factory object to create the correct SCM class from the given options and dir. """
+ def __init__(self, options, path_to_repo):
+ self.path_to_repo = path_to_repo
+ self.options = options
+
+ def make_active_scm_manager(self):
+ """ Returns a valid, usable object of type SCMRepository. """
+ if self.options.scm_mode == 'no':
+ return SCMRepository(self.path_to_repo)
+ for glbl in globals().values():
+ try:
+ if issubclass(glbl, SCMRepository):
+ the_scm = glbl(self.path_to_repo)
+ if the_scm.is_active():
+ print 'Found SCM of type:', the_scm.handles_scm_type
+ return the_scm
+ except (TypeError, AttributeError, InvalidSCMError):
+ pass
+ if self.options == 'yes':
+ return None
+ return SCMRepository(self.path_to_repo)
+
+ def make_empty_scm_manager(self, scm_type='git'):
+ """ Returns a valid, usable object of type SCMRepository for an unitialized dir. """
+ if self.options.scm_mode == 'no':
+ return SCMRepository(self.path_to_repo)
+ for glbl in globals().values():
+ try:
+ if issubclass(glbl, SCMRepository):
+ if glbl.handles_scm_type == scm_type:
+ return glbl(self.path_to_repo, is_empty=True)
+ except (TypeError, AttributeError, InvalidSCMError):
+ pass
+ if self.options == 'yes':
+ return None
+ return SCMRepository(self.path_to_repo)
+