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
|
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(
|
def init(
|
||||||
path,
|
path,
|
||||||
template=None,
|
template=None,
|
||||||
|
@ -15,17 +24,98 @@ def init(
|
||||||
args = [_git, 'init']
|
args = [_git, 'init']
|
||||||
if template is not None:
|
if template is not None:
|
||||||
args.append('--template=%s' % template)
|
args.append('--template=%s' % template)
|
||||||
env = {}
|
|
||||||
env.update(os.environ)
|
|
||||||
env['GIT_DIR'] = '.'
|
|
||||||
returncode = subprocess.call(
|
returncode = subprocess.call(
|
||||||
args=args,
|
args=args,
|
||||||
cwd=path,
|
cwd=path,
|
||||||
close_fds=True,
|
close_fds=True,
|
||||||
env=env,
|
env=dict(GIT_DIR='.'),
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
raise RuntimeError(
|
raise GitInitError('exit status %d' % returncode)
|
||||||
("Command '%r' returned non-zero exit status %d"
|
|
||||||
% (args, 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
|
from nose.tools import eq_ as eq
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from gitosis import repository
|
from gitosis import repository
|
||||||
|
|
||||||
|
@ -55,3 +56,49 @@ def test_init_templates():
|
||||||
eq(got, '#!/bin/sh\n# i can override standard templates\n')
|
eq(got, '#!/bin/sh\n# i can override standard templates\n')
|
||||||
# standard templates are there, too
|
# standard templates are there, too
|
||||||
assert os.path.isfile(os.path.join(path, 'hooks', 'pre-rebase'))
|
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