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
1diff --git a/gitubuntu/repo_builder.py b/gitubuntu/repo_builder.py
2index cb93278..04973f7 100644
3--- a/gitubuntu/repo_builder.py
4+++ b/gitubuntu/repo_builder.py
5@@ -17,6 +17,11 @@ its constructor. This allows a build in a single data structure instead of
6 having to use multiple statements to grab references to other parts of the same
7 structure.
8
9+Use a Commit object to represent a git commit (constructed with a Tree object
10+to represent the git tree contained within it), and a CommitGraph object to
11+contain and create multiple commits as needed, including ones that reference
12+each other using Placeholder objects.
13+
14 Write any object (including any children as necessary) using the write method,
15 which takes a single pygit2.Repository parameter. This returns the pygit2.Oid
16 of the top level written object.
17@@ -28,6 +33,7 @@ Objects must be treated as immutable. The result of mutating an object after it
18 __all__ = [
19 'Blob',
20 'Commit',
21+ 'CommitGraph',
22 'ExecutableBlob',
23 'Symlink',
24 'Placeholder',
25@@ -42,6 +48,13 @@ __all__ = [
26 # hashes or something?
27
28
29+DEFAULT_SIGNATURE = pygit2.Signature(
30+ name='Test Builder',
31+ email='test@example.com',
32+ time=0, # don't default to current time for hash reproducibility
33+)
34+
35+
36 class NamedMixin:
37 def __init__(self, name=None):
38 self.name = name
39@@ -122,25 +135,6 @@ class Tree(NamedMixin, WriteMixin):
40 super().__init__(**kwargs)
41 self.entries = entries
42
43- def __getitem__(self, k):
44- if k == self.name:
45- return self
46-
47- # XXX inefficient
48- for entry in self.entries.values():
49- if isinstance(entry, Placeholder):
50- # As an exception, Placeholders don't have names so cannot be
51- # located using this method.
52- continue
53- if entry.name == k:
54- return entry
55- elif isinstance(entry, type(self)):
56- try:
57- return entry[k]
58- except KeyError:
59- pass
60- raise KeyError('Object named %r not found' % k)
61-
62 def replace(self, old, new):
63 # XXX inefficient
64 for name, entry in self.entries.items():
65@@ -166,31 +160,80 @@ class Tree(NamedMixin, WriteMixin):
66
67
68 class Commit(NamedMixin, WriteMixin):
69- def __init__(self, tree, **kwargs):
70+ def __init__(self, tree, parents=None, message=None, **kwargs):
71 super().__init__(**kwargs)
72 self.tree = tree
73+ self.parents = parents or []
74+ self.message = 'Test commit' if message is None else message
75+
76+ def replace(self, old, new):
77+ for i, parent in enumerate(self.parents):
78+ if parent is old:
79+ self.parents[i] = new
80+ return
81+ raise KeyError("Cannot find %r in parents" % old)
82
83 def walk(self, parent=None):
84+ for commit_parent in self.parents:
85+ yield from commit_parent.walk(parent=self)
86+ yield from self.tree.walk(parent=self)
87 yield parent, self
88
89 def _obj_to_oid(self, repo, record=None):
90 return repo.create_commit(
91 None,
92- pygit2.Signature(name='Test Builder', email='test@example.com'),
93- pygit2.Signature(name='Test Builder', email='test@example.com'),
94- 'Test commit',
95+ DEFAULT_SIGNATURE,
96+ DEFAULT_SIGNATURE,
97+ self.message,
98 self.tree.write(repo),
99- [],
100+ [parent.write(repo, record=record) for parent in self.parents],
101 )
102
103- def __getitem__(self, k):
104- return self if self.name == k else self.tree[k]
105+
106+class CommitGraph:
107+ """Represent a graph of commits
108+
109+ We keep the commits as a list, but understand that they may relate to each
110+ other using parenting relationships. This is affectively a commit container
111+ that exists for the convenience of writing them to a git repository at
112+ once.
113+ """
114+ def __init__(self, commit_list):
115+ """Construct a CommitGraph instance
116+
117+ :param list(Commit) commit_list: the commits this CommitGraph should
118+ contain. These may be related to each other with parenting
119+ relationships, but do not have to be.
120+ """
121+ self.commit_list = commit_list
122+ replace_placeholders(self)
123+
124+ def write(self, repo, record=None):
125+ record = record or dict()
126+ written_commits = [
127+ commit.write(repo=repo, record=record)
128+ for commit
129+ in self.commit_list
130+ ]
131+ return written_commits[0]
132+
133+ def walk(self):
134+ for commit in self.commit_list:
135+ yield from commit.walk(parent=self)
136+
137+
138+def find_node(top, name):
139+ for parent, obj in top.walk():
140+ if isinstance(obj, NamedMixin):
141+ if obj.name == name:
142+ return obj
143+ raise KeyError("Object named %r not found" % name)
144
145
146 def replace_placeholders(top):
147 for parent, obj in top.walk():
148 if isinstance(obj, Placeholder):
149- parent.replace(obj, top[obj.target_name])
150+ parent.replace(obj, find_node(top, obj.target_name))
151
152
153 class TestObjectCreation(unittest.TestCase):
154@@ -224,16 +267,50 @@ class TestObjectCreation(unittest.TestCase):
155 commit = self.repo.get(commit_ref)
156 assert commit.tree_id == tree_ref
157
158+ def testCommitMessage(self):
159+ ref = Commit(Tree({}), message='foo').write(self.repo)
160+ commit = self.repo.get(ref)
161+ assert commit.message == 'foo'
162+
163+ def testCommitParents(self):
164+ tree = Tree({})
165+ top = Commit(tree, message='top')
166+ top_ref = top.write(self.repo)
167+ child_a = Commit(tree, parents=[top], message='child_a')
168+ child_a_ref = child_a.write(self.repo)
169+ child_b = Commit(tree, parents=[top], message='child_b')
170+ child_b_ref = child_b.write(self.repo)
171+ merge = Commit(tree, parents=[child_a, child_b], message='merge')
172+ merge_ref = merge.write(self.repo)
173+
174+ merge_commit = self.repo.get(merge_ref)
175+ assert merge_commit.parent_ids == [child_a_ref, child_b_ref]
176+
177+ child_a_commit = self.repo.get(child_a_ref)
178+ assert child_a_commit.parent_ids == [top_ref]
179+
180+ top_commit = self.repo.get(top_ref)
181+ assert top_commit.parent_ids == []
182+
183 def testNameSearch(self):
184 inner_tree = Tree({'foo': Blob(b'bar', name='blob')}, name='inner')
185 outer_tree = Tree({'baz': inner_tree}, name='outer')
186 commit = Commit(outer_tree, name='commit')
187- assert commit['commit'] is commit
188- assert commit['inner'] is inner_tree
189- assert commit['outer'] is outer_tree
190- assert isinstance(commit['blob'], Blob)
191+ assert find_node(commit, 'commit') is commit
192+ assert find_node(commit, 'inner') is inner_tree
193+ assert find_node(commit, 'outer') is outer_tree
194+ assert isinstance(find_node(commit, 'blob'), Blob)
195 with pytest.raises(KeyError):
196- commit['absent']
197+ find_node(commit, 'absent')
198+
199+ def testCommitGraph(self):
200+ graph = CommitGraph([
201+ Commit(Tree({}), parents=[Placeholder('parent')]),
202+ Commit(Tree({}), name='parent'),
203+ ])
204+ child_ref = graph.write(self.repo)
205+ child = self.repo.get(child_ref)
206+ assert child.parent_ids == [graph.commit_list[1].write(self.repo)]
207
208
209 @pytest.mark.parametrize('cls', [
210@@ -248,3 +325,12 @@ def test_placeholder(cls):
211 assert top.entries['foo'] is not top.entries['bar']
212 replace_placeholders(top)
213 assert top.entries['foo'] is top.entries['bar']
214+
215+def test_commit_graph_placeholder():
216+ empty_tree = Tree({})
217+ graph = CommitGraph([
218+ Commit(empty_tree, message='top', name='top'),
219+ Commit(empty_tree, message='child', parents=[Placeholder('top')]),
220+ ])
221+ replace_placeholders(graph)
222+ assert graph.commit_list[1].parents[0] is graph.commit_list[0]

Subscribers

People subscribed via source and target branches