@@ -80,38 +80,98 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
80
80
return 0 ;
81
81
}
82
82
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 ,
84
94
const struct commit_list * want )
85
95
{
86
- struct commit_list * p ;
87
-
88
96
/* was it previously marked as containing a want commit? */
89
97
if (candidate -> object .flags & TMP_MARK )
90
98
return 1 ;
91
99
/* or marked as not possibly containing a want commit? */
92
100
if (candidate -> object .flags & UNINTERESTING )
93
101
return 0 ;
94
102
/* or are we it? */
95
- if (in_commit_list (want , candidate ))
103
+ if (in_commit_list (want , candidate )) {
104
+ candidate -> object .flags |= TMP_MARK ;
96
105
return 1 ;
106
+ }
97
107
98
108
if (parse_commit (candidate ) < 0 )
99
109
return 0 ;
100
110
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 ;
110
112
}
111
113
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 )
113
139
{
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 );
115
175
}
116
176
117
177
static void show_tag_lines (const unsigned char * sha1 , int lines )
0 commit comments