61
63
class GitBranchBuilder(object):
63
def __init__(self, stream):
65
def __init__(self, stream=None):
64
66
self.commit_info = []
65
67
self.stream = stream
67
70
self._branch = 'refs/head/master'
72
self._marks_file = tempfile.NamedTemporaryFile(
73
prefix='tmp-git-marks')
74
self._process = subprocess.Popen(
75
['git', 'fast-import', '--quiet',
76
# GIT doesn't support '--export-marks foo'
77
# it only supports '--export-marks=foo'
78
# And gives a 'unknown option' otherwise.
79
'--export-marks='+self._marks_file.name,
81
stdout=subprocess.PIPE,
82
stderr=subprocess.PIPE,
83
stdin=subprocess.PIPE,
85
self.stream = self._process.stdin
69
89
def set_branch(self, branch):
70
90
"""Set the branch we are committing."""
71
91
self._branch = branch
93
def _write(self, text):
95
self.stream.write(text)
97
if self._process is None:
99
raise errors.GitCommandError(self._process.returncode,
101
self._process.stderr.read())
103
def _writelines(self, lines):
105
self.stream.writelines(lines)
107
if self._process is None:
109
raise errors.GitCommandError(self._process.returncode,
111
self._process.stderr.read())
73
113
def _create_blob(self, content):
74
114
self._counter += 1
75
self.stream.write('blob\n')
76
self.stream.write('mark :%d\n' % (self._counter,))
77
self.stream.write('data %d\n' % (len(content),))
78
self.stream.write(content)
79
self.stream.write('\n')
115
self._write('blob\n')
116
self._write('mark :%d\n' % (self._counter,))
117
self._write('data %d\n' % (len(content),))
80
120
return self._counter
82
122
def set_file(self, path, content, executable):
99
139
"""This will delete files or symlinks at the given location."""
100
140
self.commit_info.append('D %s\n' % (path.encode('utf-8'),))
143
# TODO: Author timestamp+timezone
102
144
def commit(self, committer, message, timestamp=None,
103
timezone='+0000', author=None):
145
timezone='+0000', author=None,
146
merge=None, base=None):
147
"""Commit the new content.
149
:param committer: The name and address for the committer
150
:param message: The commit message
151
:param timestamp: The timestamp for the commit
152
:param timezone: The timezone of the commit, such as '+0000' or '-1000'
153
:param author: The name and address of the author (if different from
155
:param merge: A list of marks if this should merge in another commit
156
:param base: An id for the base revision (primary parent) if that
157
is not the last commit.
158
:return: A mark which can be used in the future to reference this
104
161
self._counter += 1
105
162
mark = self._counter
106
self.stream.write('commit %s\n' % (branch,))
107
self.stream.write('mark :%d\n' % (mark,))
108
self.stream.write('committer %s %s %s\n'
109
% (committer, timestamp, timezone))
163
if timestamp is None:
164
timestamp = int(time.time())
165
self._write('commit %s\n' % (self._branch,))
166
self._write('mark :%d\n' % (mark,))
167
self._write('committer %s %s %s\n'
168
% (committer, timestamp, timezone))
110
169
message = message.encode('UTF-8')
111
self.stream.write('data %d\n' % (len(message),))
112
self.stream.write(message)
113
self.stream.write('\n')
114
self.stream.writelines(self.commit_info)
115
self.stream.write('\n')
170
self._write('data %d\n' % (len(message),))
174
self._write('from :%d\n' % (base,))
175
if merge is not None:
177
self._write('merge :%d\n' % (m,))
178
self._writelines(self.commit_info)
116
180
self.commit_info = []
120
class GitBranchBuilder(object):
121
"""This uses git-fast-import to build up something directly."""
123
def __init__(self, git_dir):
124
self.git_dir = git_dir
184
"""We are finished building, close the stream, get the id mapping"""
186
if self._process is None:
188
if self._process.wait() != 0:
189
raise errors.GitCommandError(self._process.returncode,
191
self._process.stderr.read())
192
self._marks_file.seek(0)
194
for line in self._marks_file:
195
mark, shasum = line.split()
196
assert mark.startswith(':')
197
mapping[int(mark[1:])] = shasum
198
self._marks_file.close()
127
202
def test_suite():