Merge ~racb/git-ubuntu:commit-graph-builder into git-ubuntu:master

Proposed by Robie Basak
Status: Merged
Merged at revision: 94dbfe16b413bf16fb1fb203d7a96976c2e6259d
Proposed branch: ~racb/git-ubuntu:commit-graph-builder
Merge into: git-ubuntu:master
Diff against target: 222 lines (+118/-32)
1 file modified
gitubuntu/repo_builder.py (+118/-32)
Reviewer Review Type Date Requested Status
Nish Aravamudan Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+337280@code.launchpad.net

Commit message

Make Jenkins happy

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:94dbfe16b413bf16fb1fb203d7a96976c2e6259d
https://jenkins.ubuntu.com/server/job/git-ubuntu-ci/280/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Style Check
    SUCCESS: Unit Tests
    SUCCESS: Integration Tests
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/git-ubuntu-ci/280/rebuild

review: Approve (continuous-integration)
Revision history for this message
Nish Aravamudan (nacc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/gitubuntu/repo_builder.py b/gitubuntu/repo_builder.py
index cb93278..04973f7 100644
--- a/gitubuntu/repo_builder.py
+++ b/gitubuntu/repo_builder.py
@@ -17,6 +17,11 @@ its constructor. This allows a build in a single data structure instead of
17having to use multiple statements to grab references to other parts of the same17having to use multiple statements to grab references to other parts of the same
18structure.18structure.
1919
20Use a Commit object to represent a git commit (constructed with a Tree object
21to represent the git tree contained within it), and a CommitGraph object to
22contain and create multiple commits as needed, including ones that reference
23each other using Placeholder objects.
24
20Write any object (including any children as necessary) using the write method,25Write any object (including any children as necessary) using the write method,
21which takes a single pygit2.Repository parameter. This returns the pygit2.Oid26which takes a single pygit2.Repository parameter. This returns the pygit2.Oid
22of the top level written object.27of the top level written object.
@@ -28,6 +33,7 @@ Objects must be treated as immutable. The result of mutating an object after it
28__all__ = [33__all__ = [
29 'Blob',34 'Blob',
30 'Commit',35 'Commit',
36 'CommitGraph',
31 'ExecutableBlob',37 'ExecutableBlob',
32 'Symlink',38 'Symlink',
33 'Placeholder',39 'Placeholder',
@@ -42,6 +48,13 @@ __all__ = [
42# hashes or something?48# hashes or something?
4349
4450
51DEFAULT_SIGNATURE = pygit2.Signature(
52 name='Test Builder',
53 email='test@example.com',
54 time=0, # don't default to current time for hash reproducibility
55)
56
57
45class NamedMixin:58class NamedMixin:
46 def __init__(self, name=None):59 def __init__(self, name=None):
47 self.name = name60 self.name = name
@@ -122,25 +135,6 @@ class Tree(NamedMixin, WriteMixin):
122 super().__init__(**kwargs)135 super().__init__(**kwargs)
123 self.entries = entries136 self.entries = entries
124137
125 def __getitem__(self, k):
126 if k == self.name:
127 return self
128
129 # XXX inefficient
130 for entry in self.entries.values():
131 if isinstance(entry, Placeholder):
132 # As an exception, Placeholders don't have names so cannot be
133 # located using this method.
134 continue
135 if entry.name == k:
136 return entry
137 elif isinstance(entry, type(self)):
138 try:
139 return entry[k]
140 except KeyError:
141 pass
142 raise KeyError('Object named %r not found' % k)
143
144 def replace(self, old, new):138 def replace(self, old, new):
145 # XXX inefficient139 # XXX inefficient
146 for name, entry in self.entries.items():140 for name, entry in self.entries.items():
@@ -166,31 +160,80 @@ class Tree(NamedMixin, WriteMixin):
166160
167161
168class Commit(NamedMixin, WriteMixin):162class Commit(NamedMixin, WriteMixin):
169 def __init__(self, tree, **kwargs):163 def __init__(self, tree, parents=None, message=None, **kwargs):
170 super().__init__(**kwargs)164 super().__init__(**kwargs)
171 self.tree = tree165 self.tree = tree
166 self.parents = parents or []
167 self.message = 'Test commit' if message is None else message
168
169 def replace(self, old, new):
170 for i, parent in enumerate(self.parents):
171 if parent is old:
172 self.parents[i] = new
173 return
174 raise KeyError("Cannot find %r in parents" % old)
172175
173 def walk(self, parent=None):176 def walk(self, parent=None):
177 for commit_parent in self.parents:
178 yield from commit_parent.walk(parent=self)
179 yield from self.tree.walk(parent=self)
174 yield parent, self180 yield parent, self
175181
176 def _obj_to_oid(self, repo, record=None):182 def _obj_to_oid(self, repo, record=None):
177 return repo.create_commit(183 return repo.create_commit(
178 None,184 None,
179 pygit2.Signature(name='Test Builder', email='test@example.com'),185 DEFAULT_SIGNATURE,
180 pygit2.Signature(name='Test Builder', email='test@example.com'),186 DEFAULT_SIGNATURE,
181 'Test commit',187 self.message,
182 self.tree.write(repo),188 self.tree.write(repo),
183 [],189 [parent.write(repo, record=record) for parent in self.parents],
184 )190 )
185191
186 def __getitem__(self, k):192
187 return self if self.name == k else self.tree[k]193class CommitGraph:
194 """Represent a graph of commits
195
196 We keep the commits as a list, but understand that they may relate to each
197 other using parenting relationships. This is affectively a commit container
198 that exists for the convenience of writing them to a git repository at
199 once.
200 """
201 def __init__(self, commit_list):
202 """Construct a CommitGraph instance
203
204 :param list(Commit) commit_list: the commits this CommitGraph should
205 contain. These may be related to each other with parenting
206 relationships, but do not have to be.
207 """
208 self.commit_list = commit_list
209 replace_placeholders(self)
210
211 def write(self, repo, record=None):
212 record = record or dict()
213 written_commits = [
214 commit.write(repo=repo, record=record)
215 for commit
216 in self.commit_list
217 ]
218 return written_commits[0]
219
220 def walk(self):
221 for commit in self.commit_list:
222 yield from commit.walk(parent=self)
223
224
225def find_node(top, name):
226 for parent, obj in top.walk():
227 if isinstance(obj, NamedMixin):
228 if obj.name == name:
229 return obj
230 raise KeyError("Object named %r not found" % name)
188231
189232
190def replace_placeholders(top):233def replace_placeholders(top):
191 for parent, obj in top.walk():234 for parent, obj in top.walk():
192 if isinstance(obj, Placeholder):235 if isinstance(obj, Placeholder):
193 parent.replace(obj, top[obj.target_name])236 parent.replace(obj, find_node(top, obj.target_name))
194237
195238
196class TestObjectCreation(unittest.TestCase):239class TestObjectCreation(unittest.TestCase):
@@ -224,16 +267,50 @@ class TestObjectCreation(unittest.TestCase):
224 commit = self.repo.get(commit_ref)267 commit = self.repo.get(commit_ref)
225 assert commit.tree_id == tree_ref268 assert commit.tree_id == tree_ref
226269
270 def testCommitMessage(self):
271 ref = Commit(Tree({}), message='foo').write(self.repo)
272 commit = self.repo.get(ref)
273 assert commit.message == 'foo'
274
275 def testCommitParents(self):
276 tree = Tree({})
277 top = Commit(tree, message='top')
278 top_ref = top.write(self.repo)
279 child_a = Commit(tree, parents=[top], message='child_a')
280 child_a_ref = child_a.write(self.repo)
281 child_b = Commit(tree, parents=[top], message='child_b')
282 child_b_ref = child_b.write(self.repo)
283 merge = Commit(tree, parents=[child_a, child_b], message='merge')
284 merge_ref = merge.write(self.repo)
285
286 merge_commit = self.repo.get(merge_ref)
287 assert merge_commit.parent_ids == [child_a_ref, child_b_ref]
288
289 child_a_commit = self.repo.get(child_a_ref)
290 assert child_a_commit.parent_ids == [top_ref]
291
292 top_commit = self.repo.get(top_ref)
293 assert top_commit.parent_ids == []
294
227 def testNameSearch(self):295 def testNameSearch(self):
228 inner_tree = Tree({'foo': Blob(b'bar', name='blob')}, name='inner')296 inner_tree = Tree({'foo': Blob(b'bar', name='blob')}, name='inner')
229 outer_tree = Tree({'baz': inner_tree}, name='outer')297 outer_tree = Tree({'baz': inner_tree}, name='outer')
230 commit = Commit(outer_tree, name='commit')298 commit = Commit(outer_tree, name='commit')
231 assert commit['commit'] is commit299 assert find_node(commit, 'commit') is commit
232 assert commit['inner'] is inner_tree300 assert find_node(commit, 'inner') is inner_tree
233 assert commit['outer'] is outer_tree301 assert find_node(commit, 'outer') is outer_tree
234 assert isinstance(commit['blob'], Blob)302 assert isinstance(find_node(commit, 'blob'), Blob)
235 with pytest.raises(KeyError):303 with pytest.raises(KeyError):
236 commit['absent']304 find_node(commit, 'absent')
305
306 def testCommitGraph(self):
307 graph = CommitGraph([
308 Commit(Tree({}), parents=[Placeholder('parent')]),
309 Commit(Tree({}), name='parent'),
310 ])
311 child_ref = graph.write(self.repo)
312 child = self.repo.get(child_ref)
313 assert child.parent_ids == [graph.commit_list[1].write(self.repo)]
237314
238315
239@pytest.mark.parametrize('cls', [316@pytest.mark.parametrize('cls', [
@@ -248,3 +325,12 @@ def test_placeholder(cls):
248 assert top.entries['foo'] is not top.entries['bar']325 assert top.entries['foo'] is not top.entries['bar']
249 replace_placeholders(top)326 replace_placeholders(top)
250 assert top.entries['foo'] is top.entries['bar']327 assert top.entries['foo'] is top.entries['bar']
328
329def test_commit_graph_placeholder():
330 empty_tree = Tree({})
331 graph = CommitGraph([
332 Commit(empty_tree, message='top', name='top'),
333 Commit(empty_tree, message='child', parents=[Placeholder('top')]),
334 ])
335 replace_placeholders(graph)
336 assert graph.commit_list[1].parents[0] is graph.commit_list[0]

Subscribers

People subscribed via source and target branches