Add utilities for fast-import, exporting repository.
Redo subprocess error handling.
This commit is contained in:
parent
fd11351859
commit
0dbee1fadd
|
@ -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 RuntimeError(
|
||||
("Command '%r' returned non-zero exit status %d"
|
||||
% (args, returncode)),
|
||||
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 GitCheckoutIndexError('exit status %d' % returncode)
|
||||
|
|
|
@ -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:], [])
|
||||
|
|
Loading…
Reference in a new issue