summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <lars.wirzenius@codethink.co.uk>2013-08-01 16:15:45 +0000
committerLars Wirzenius <lars.wirzenius@codethink.co.uk>2013-08-06 16:28:16 +0100
commited05a9ff622e6ddbff3406e05c4f611e77a5231f (patch)
treed7b1f73b2c442e9f853b6fecde0e64f4426abd4c
parent933d8fe56e82e971f5bef16d523ceaedf8b27f4b (diff)
downloaddefinitions-ed05a9ff622e6ddbff3406e05c4f611e77a5231f.tar.gz
Add GitDirectory class
-rw-r--r--morphlib/__init__.py1
-rw-r--r--morphlib/gitdir.py131
-rw-r--r--morphlib/gitdir_tests.py66
3 files changed, 198 insertions, 0 deletions
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)
+