@@ -80,38 +80,98 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
8080 return 0 ;
8181}
8282
83- static int contains_recurse (struct commit * candidate ,
83+ enum contains_result {
84+ CONTAINS_UNKNOWN = -1 ,
85+ CONTAINS_NO = 0 ,
86+ CONTAINS_YES = 1 ,
87+ };
88+
89+ /*
90+ * Test whether the candidate or one of its parents is contained in the list.
91+ * Do not recurse to find out, though, but return -1 if inconclusive.
92+ */
93+ static enum contains_result contains_test (struct commit * candidate ,
8494 const struct commit_list * want )
8595{
86- struct commit_list * p ;
87-
8896 /* was it previously marked as containing a want commit? */
8997 if (candidate -> object .flags & TMP_MARK )
9098 return 1 ;
9199 /* or marked as not possibly containing a want commit? */
92100 if (candidate -> object .flags & UNINTERESTING )
93101 return 0 ;
94102 /* or are we it? */
95- if (in_commit_list (want , candidate ))
103+ if (in_commit_list (want , candidate )) {
104+ candidate -> object .flags |= TMP_MARK ;
96105 return 1 ;
106+ }
97107
98108 if (parse_commit (candidate ) < 0 )
99109 return 0 ;
100110
101- /* Otherwise recurse and mark ourselves for future traversals. */
102- for (p = candidate -> parents ; p ; p = p -> next ) {
103- if (contains_recurse (p -> item , want )) {
104- candidate -> object .flags |= TMP_MARK ;
105- return 1 ;
106- }
107- }
108- candidate -> object .flags |= UNINTERESTING ;
109- return 0 ;
111+ return -1 ;
110112}
111113
112- static int contains (struct commit * candidate , const struct commit_list * want )
114+ /*
115+ * Mimicking the real stack, this stack lives on the heap, avoiding stack
116+ * overflows.
117+ *
118+ * At each recursion step, the stack items points to the commits whose
119+ * ancestors are to be inspected.
120+ */
121+ struct stack {
122+ int nr , alloc ;
123+ struct stack_entry {
124+ struct commit * commit ;
125+ struct commit_list * parents ;
126+ } * stack ;
127+ };
128+
129+ static void push_to_stack (struct commit * candidate , struct stack * stack )
130+ {
131+ int index = stack -> nr ++ ;
132+ ALLOC_GROW (stack -> stack , stack -> nr , stack -> alloc );
133+ stack -> stack [index ].commit = candidate ;
134+ stack -> stack [index ].parents = candidate -> parents ;
135+ }
136+
137+ static enum contains_result contains (struct commit * candidate ,
138+ const struct commit_list * want )
113139{
114- return contains_recurse (candidate , want );
140+ struct stack stack = { 0 , 0 , NULL };
141+ int result = contains_test (candidate , want );
142+
143+ if (result != CONTAINS_UNKNOWN )
144+ return result ;
145+
146+ push_to_stack (candidate , & stack );
147+ while (stack .nr ) {
148+ struct stack_entry * entry = & stack .stack [stack .nr - 1 ];
149+ struct commit * commit = entry -> commit ;
150+ struct commit_list * parents = entry -> parents ;
151+
152+ if (!parents ) {
153+ commit -> object .flags |= UNINTERESTING ;
154+ stack .nr -- ;
155+ }
156+ /*
157+ * If we just popped the stack, parents->item has been marked,
158+ * therefore contains_test will return a meaningful 0 or 1.
159+ */
160+ else switch (contains_test (parents -> item , want )) {
161+ case CONTAINS_YES :
162+ commit -> object .flags |= TMP_MARK ;
163+ stack .nr -- ;
164+ break ;
165+ case CONTAINS_NO :
166+ entry -> parents = parents -> next ;
167+ break ;
168+ case CONTAINS_UNKNOWN :
169+ push_to_stack (parents -> item , & stack );
170+ break ;
171+ }
172+ }
173+ free (stack .stack );
174+ return contains_test (candidate , want );
115175}
116176
117177static void show_tag_lines (const unsigned char * sha1 , int lines )
0 commit comments