33# file, You can obtain one at http://mozilla.org/MPL/2.0/.
44import hashlib
55import json
6+ from collections import defaultdict
67from copy import deepcopy
78
89from landoapi .phabricator import (
@@ -59,7 +60,7 @@ def validate_hunk(hunk):
5960 assert isinstance (hunk ["corpus" ], str )
6061 lines = hunk ["corpus" ].splitlines ()
6162 assert len (lines ) > 0
62- assert all ([l [0 ] in (" " , "-" , "+" ) for l in lines ])
63+ assert all ([line [0 ] in (" " , "-" , "+" ) for line in lines ])
6364
6465 return True
6566
@@ -96,6 +97,51 @@ def validate_change(change):
9697 return True
9798
9899
100+ def get_stack (phid , phabdouble ):
101+ phids = set ()
102+ new_phids = {phid }
103+ edges = []
104+
105+ # Repeatedly request all related edges, adding connected revisions
106+ # each time until no new revisions are found.
107+ # NOTE: this was adapted from previous implementation of build_stack_graph.
108+ while new_phids :
109+ phids .update (new_phids )
110+ edges = [
111+ edge
112+ for edge in phabdouble ._edges
113+ if edge ["sourcePHID" ] in phids
114+ and edge ["edgeType" ] in ("revision.parent" , "revision.child" )
115+ ]
116+ new_phids = set ()
117+ for edge in edges :
118+ new_phids .add (edge ["sourcePHID" ])
119+ new_phids .add (edge ["destinationPHID" ])
120+
121+ new_phids = new_phids - phids
122+
123+ # Treat the stack like a commit DAG, we only care about edges going
124+ # from child to parent. This is enough to represent the graph.
125+ edges = {
126+ (edge ["sourcePHID" ], edge ["destinationPHID" ])
127+ for edge in edges
128+ if edge ["edgeType" ] == "revision.parent"
129+ }
130+
131+ stack_graph = defaultdict (list )
132+ sources = [source for source , dest in edges ]
133+ for source , dest in edges :
134+ # Check that destination phid has a corresponding source phid.
135+ if dest not in sources :
136+ # We are at a root node.
137+ stack_graph [dest ] = []
138+ stack_graph [source ].append (dest )
139+ if not stack_graph :
140+ # There is only one node, the root node.
141+ stack_graph [phid ] = []
142+ return dict (stack_graph )
143+
144+
99145class PhabricatorDouble :
100146 """Phabricator test double.
101147
@@ -190,6 +236,30 @@ def api_object_for(self, mock_object: dict, **kwargs) -> dict:
190236 def get_phabricator_client ():
191237 return PhabricatorClient ("https://localhost" , "DOESNT-MATTER" )
192238
239+ def update_revision_dependencies (self , phid : str , depends_on : list [str ]):
240+ """Updates edges of `phid` so they match `depends_on`."""
241+ # Remove all previous edges related to this revision.
242+ def philter (edge ):
243+ return phid not in (edge ["sourcePHID" ], edge ["destinationPHID" ])
244+
245+ self ._edges = list (filter (philter , self ._edges ))
246+
247+ for rev in depends_on :
248+ self ._edges .append (
249+ {
250+ "edgeType" : "revision.parent" ,
251+ "sourcePHID" : phid ,
252+ "destinationPHID" : rev ["phid" ],
253+ }
254+ )
255+ self ._edges .append (
256+ {
257+ "edgeType" : "revision.child" ,
258+ "sourcePHID" : rev ["phid" ],
259+ "destinationPHID" : phid ,
260+ }
261+ )
262+
193263 def revision (
194264 self ,
195265 * ,
@@ -905,6 +975,7 @@ def to_response(i):
905975 "fields" : {
906976 "title" : i ["title" ],
907977 "authorPHID" : i ["authorPHID" ],
978+ "stackGraph" : i ["stackGraph" ],
908979 "status" : {
909980 "value" : i ["status" ].value ,
910981 "name" : i ["status" ].output_name ,
@@ -913,6 +984,7 @@ def to_response(i):
913984 },
914985 "repositoryPHID" : i ["repositoryPHID" ],
915986 "diffPHID" : diffs [- 1 ]["phid" ],
987+ "diffID" : diffs [- 1 ]["id" ],
916988 "summary" : i ["summary" ],
917989 "dateCreated" : i ["dateCreated" ],
918990 "dateModified" : i ["dateModified" ],
@@ -955,7 +1027,10 @@ def to_response(i):
9551027
9561028 return deepcopy (resp )
9571029
958- items = [r for r in self ._revisions ]
1030+ items = []
1031+ for revision in self ._revisions :
1032+ revision ["stackGraph" ] = get_stack (revision ["phid" ], self )
1033+ items .append (revision )
9591034
9601035 if constraints and "ids" in constraints :
9611036 items = [i for i in items if i ["id" ] in constraints ["ids" ]]
0 commit comments