aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommi Virtanen <tv@eagain.net>2007-09-01 17:47:42 -0700
committerTommi Virtanen <tv@eagain.net>2007-09-01 18:06:45 -0700
commit0dbee1faddc65533c1d485f4b6d6693852db07c6 (patch)
tree6607cb1ea1bc9789a9b18a18500994b03e98a4f2
parentUse separate temp directories for separate tests. (diff)
downloadgitosis-dakkar-0dbee1faddc65533c1d485f4b6d6693852db07c6.tar.gz
gitosis-dakkar-0dbee1faddc65533c1d485f4b6d6693852db07c6.tar.bz2
gitosis-dakkar-0dbee1faddc65533c1d485f4b6d6693852db07c6.zip
Add utilities for fast-import, exporting repository.
Redo subprocess error handling.
-rw-r--r--gitosis/repository.py106
-rw-r--r--gitosis/test/test_repository.py47
2 files changed, 145 insertions, 8 deletions
diff --git a/gitosis/repository.py b/gitosis/repository.py
index 17a8818..764c980 100644
--- a/gitosis/repository.py
+++ b/gitosis/repository.py
@@ -3,6 +3,15 @@ import subprocess
from gitosis import util
+class GitError(Exception):
+ """git failed"""
+
+ def __str__(self):
+ return '%s: %s' % (self.__doc__, ': '.join(self.args))
+
+class GitInitError(Exception):
+ """git init failed"""
+
def init(
path,
template=None,
@@ -15,17 +24,98 @@ def init(
args = [_git, 'init']
if template is not None:
args.append('--template=%s' % template)
- env = {}
- env.update(os.environ)
- env['GIT_DIR'] = '.'
returncode = subprocess.call(
args=args,
cwd=path,
close_fds=True,
- env=env,
+ env=dict(GIT_DIR='.'),
+ )
+ if returncode != 0:
+ raise GitInitError('exit status %d' % returncode)
+
+
+class GitFastImportError(GitError):
+ """git fast-import failed"""
+ pass
+
+def fast_import(
+ git_dir,
+ commit_msg,
+ committer,
+ files,
+ ):
+ """
+ Create an initial commit.
+ """
+ init(path=git_dir)
+ child = subprocess.Popen(
+ args=['git', 'fast-import', '--quiet', '--date-format=now'],
+ cwd=git_dir,
+ stdin=subprocess.PIPE,
+ close_fds=True,
+ env=dict(GIT_DIR=git_dir),
+ )
+ files = list(files)
+ for index, (path, content) in enumerate(files):
+ child.stdin.write("""\
+blob
+mark :%(mark)d
+data %(len)d
+%(content)s
+""" % dict(
+ mark=index+1,
+ len=len(content),
+ content=content,
+ ))
+ child.stdin.write("""\
+commit refs/heads/master
+committer %(committer)s now
+data %(commit_msg_len)d
+%(commit_msg)s
+""" % dict(
+ committer=committer,
+ commit_msg_len=len(commit_msg),
+ commit_msg=commit_msg,
+ ))
+ for index, (path, content) in enumerate(files):
+ child.stdin.write('M 100644 :%d %s\n' % (index+1, path))
+ child.stdin.close()
+ returncode = child.wait()
+ if returncode != 0:
+ raise GitFastImportError(
+ 'git fast-import failed', 'exit status %d' % returncode)
+
+class GitExportError(GitError):
+ """Export failed"""
+ pass
+
+class GitReadTreeError(GitExportError):
+ """git read-tree failed"""
+
+class GitCheckoutIndexError(GitExportError):
+ """git checkout-index failed"""
+
+def export(git_dir, path):
+ # it's a literal prefix for git, a trailing slash is needed to
+ # extract to the subdirectory
+ path = os.path.join(path, '')
+ returncode = subprocess.call(
+ args=['git', 'read-tree', 'HEAD'],
+ close_fds=True,
+ env=dict(GIT_DIR=git_dir),
+ )
+ if returncode != 0:
+ raise GitReadTreeError('exit status %d' % returncode)
+ returncode = subprocess.call(
+ args=[
+ 'git',
+ 'checkout-index',
+ '-a',
+ '-f',
+ '--prefix=%s' % path,
+ ],
+ close_fds=True,
+ env=dict(GIT_DIR=git_dir),
)
if returncode != 0:
- raise RuntimeError(
- ("Command '%r' returned non-zero exit status %d"
- % (args, returncode)),
- )
+ raise GitCheckoutIndexError('exit status %d' % returncode)
diff --git a/gitosis/test/test_repository.py b/gitosis/test/test_repository.py
index c9fbc58..79d1561 100644
--- a/gitosis/test/test_repository.py
+++ b/gitosis/test/test_repository.py
@@ -1,6 +1,7 @@
from nose.tools import eq_ as eq
import os
+import subprocess
from gitosis import repository
@@ -55,3 +56,49 @@ def test_init_templates():
eq(got, '#!/bin/sh\n# i can override standard templates\n')
# standard templates are there, too
assert os.path.isfile(os.path.join(path, 'hooks', 'pre-rebase'))
+
+def test_export_simple():
+ tmp = maketemp()
+ git_dir = os.path.join(tmp, 'repo.git')
+ repository.init(path=git_dir)
+ repository.fast_import(
+ git_dir=git_dir,
+ committer='John Doe <jdoe@example.com>',
+ commit_msg="""\
+Reverse the polarity of the neutron flow.
+
+Frobitz the quux and eschew obfuscation.
+""",
+ files=[
+ ('foo', 'content'),
+ ('bar/quux', 'another'),
+ ],
+ )
+ export = os.path.join(tmp, 'export')
+ repository.export(git_dir=git_dir, path=export)
+ eq(sorted(os.listdir(export)),
+ sorted(['foo', 'bar']))
+ eq(readFile(os.path.join(export, 'foo')), 'content')
+ eq(os.listdir(os.path.join(export, 'bar')), ['quux'])
+ eq(readFile(os.path.join(export, 'bar', 'quux')), 'another')
+ child = subprocess.Popen(
+ args=['git', 'cat-file', 'commit', 'HEAD'],
+ cwd=git_dir,
+ stdout=subprocess.PIPE,
+ close_fds=True,
+ env=dict(GIT_DIR=git_dir),
+ )
+ got = child.stdout.read().splitlines()
+ returncode = child.wait()
+ if returncode != 0:
+ raise RuntimeError('git exit status %d' % returncode)
+ eq(got[0].split(None, 1)[0], 'tree')
+ eq(got[1].rsplit(None, 2)[0],
+ 'author John Doe <jdoe@example.com>')
+ eq(got[2].rsplit(None, 2)[0],
+ 'committer John Doe <jdoe@example.com>')
+ eq(got[3], '')
+ eq(got[4], 'Reverse the polarity of the neutron flow.')
+ eq(got[5], '')
+ eq(got[6], 'Frobitz the quux and eschew obfuscation.')
+ eq(got[7:], [])