From ed05a9ff622e6ddbff3406e05c4f611e77a5231f Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Thu, 1 Aug 2013 16:15:45 +0000 Subject: Add GitDirectory class --- morphlib/__init__.py | 1 + morphlib/gitdir.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++ morphlib/gitdir_tests.py | 66 ++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 morphlib/gitdir.py create mode 100644 morphlib/gitdir_tests.py diff --git a/morphlib/__init__.py b/morphlib/__init__.py index 6b2a1cd7..787ee269 100644 --- a/morphlib/__init__.py +++ b/morphlib/__init__.py @@ -59,6 +59,7 @@ import cachekeycomputer import extractedtarball import fsutils import git +import gitdir import localartifactcache import localrepocache import mountableimage diff --git a/morphlib/gitdir.py b/morphlib/gitdir.py new file mode 100644 index 00000000..2bf74437 --- /dev/null +++ b/morphlib/gitdir.py @@ -0,0 +1,131 @@ +# Copyright (C) 2013 Codethink Limited +# +# This program 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; version 2 of the License. +# +# This program 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# =*= License: GPL-2 =*= + + +import cliapp + +import morphlib + + +class GitDirectory(object): + + '''Represent a git working tree + .git directory. + + This class represents a directory that is the result of a + "git clone". It includes both the .git subdirectory and + the working tree. It is a thin abstraction, meant to make + it easier to do certain git operations. + + ''' + + def __init__(self, dirname): + self.dirname = dirname + + def _runcmd(self, argv, **kwargs): + '''Run a command at the root of the git directory. + + See cliapp.runcmd for arguments. + + Do NOT use this from outside the class. Add more public + methods for specific git operations instead. + + ''' + + return cliapp.runcmd(argv, cwd=self.dirname, **kwargs) + + def checkout(self, branch_name): # pragma: no cover + '''Check out a git branch.''' + self._runcmd(['git', 'checkout', branch_name]) + + def branch(self, new_branch_name, base_ref): # pragma: no cover + '''Create a git branch based on an existing ref. + + This does not automatically check out the branch. + + base_ref may be None, in which case the current branch is used. + + ''' + + argv = ['git', 'branch', new_branch_name] + if base_ref is not None: + argv.append(base_ref) + self._runcmd(argv) + + def update_remotes(self): # pragma: no cover + '''Update remotes.''' + self._runcmd(['git', 'remote', 'update', '--prune']) + + def update_submodules(self, app): # pragma: no cover + '''Change .gitmodules URLs, and checkout submodules.''' + morphlib.git.update_submodules(app, self.dirname) + + def set_config(self, key, value): + '''Set a git repository configuration variable. + + The key must have at least one period in it: foo.bar for example, + not just foo. The part before the first period is interpreted + by git as a section name. + + ''' + + self._runcmd(['git', 'config', key, value]) + + def get_config(self, key): + '''Return value for a git repository configuration variable.''' + + value = self._runcmd(['git', 'config', key]) + return value.strip() + + def set_remote_fetch_url(self, remote_name, url): + '''Set the fetch URL for a remote.''' + self._runcmd(['git', 'remote', 'set-url', remote_name, url]) + + def get_remote_fetch_url(self, remote_name): + '''Return the fetch URL for a given remote.''' + output = self._runcmd(['git', 'remote', '-v']) + for line in output.splitlines(): + words = line.split() + if (len(words) == 3 and + words[0] == remote_name and + words[2] == '(fetch)'): + return words[1] + return None + + def update_remotes(self): # pragma: no cover + '''Run "git remote update --prune".''' + self._runcmd(['git', 'remote', 'update', '--prune']) + + +def init(dirname): + '''Initialise a new git repository.''' + + gd = GitDirectory(dirname) + gd._runcmd(['git', 'init']) + return gd + + +def clone_from_cached_repo(cached_repo, dirname, ref): # pragma: no cover + '''Clone a CachedRepo into the desired directory. + + The given ref is checked out (or git's default branch is checked out + if ref is None). + + ''' + + cached_repo.clone_checkout(ref, dirname) + return GitDirectory(dirname) + diff --git a/morphlib/gitdir_tests.py b/morphlib/gitdir_tests.py new file mode 100644 index 00000000..2494981a --- /dev/null +++ b/morphlib/gitdir_tests.py @@ -0,0 +1,66 @@ +# Copyright (C) 2013 Codethink Limited +# +# This program 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; version 2 of the License. +# +# This program 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# =*= License: GPL-2 =*= + + +import os +import shutil +import tempfile +import unittest + +import morphlib + + +class GitDirectoryTests(unittest.TestCase): + + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.dirname = os.path.join(self.tempdir, 'foo') + + def tearDown(self): + shutil.rmtree(self.tempdir) + + def fake_git_clone(self): + os.mkdir(self.dirname) + os.mkdir(os.path.join(self.dirname, '.git')) + + def test_has_dirname_attribute(self): + self.fake_git_clone() + gitdir = morphlib.gitdir.GitDirectory(self.dirname) + self.assertEqual(gitdir.dirname, self.dirname) + + def test_runs_command_in_right_directory(self): + self.fake_git_clone() + gitdir = morphlib.gitdir.GitDirectory(self.dirname) + output = gitdir._runcmd(['pwd']) + self.assertEqual(output.strip(), self.dirname) + + def test_sets_and_gets_configuration(self): + os.mkdir(self.dirname) + gitdir = morphlib.gitdir.init(self.dirname) + gitdir.set_config('foo.bar', 'yoyo') + self.assertEqual(gitdir.get_config('foo.bar'), 'yoyo') + + def test_sets_remote(self): + os.mkdir(self.dirname) + gitdir = morphlib.gitdir.init(self.dirname) + self.assertEqual(gitdir.get_remote_fetch_url('origin'), None) + + gitdir._runcmd(['git', 'remote', 'add', 'origin', 'foobar']) + url = 'git://git.example.com/foo' + gitdir.set_remote_fetch_url('origin', url) + self.assertEqual(gitdir.get_remote_fetch_url('origin'), url) + -- cgit v1.2.1