@@ -73,38 +73,91 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
73
73
return 0 ;
74
74
}
75
75
76
- static int contains_recurse (struct commit * candidate ,
76
+ /*
77
+ * Test whether the candidate or one of its parents is contained in the list.
78
+ * Do not recurse to find out, though, but return -1 if inconclusive.
79
+ */
80
+ static int contains_test (struct commit * candidate ,
77
81
const struct commit_list * want )
78
82
{
79
- struct commit_list * p ;
80
-
81
83
/* was it previously marked as containing a want commit? */
82
84
if (candidate -> object .flags & TMP_MARK )
83
85
return 1 ;
84
86
/* or marked as not possibly containing a want commit? */
85
87
if (candidate -> object .flags & UNINTERESTING )
86
88
return 0 ;
87
89
/* or are we it? */
88
- if (in_commit_list (want , candidate ))
90
+ if (in_commit_list (want , candidate )) {
91
+ candidate -> object .flags |= TMP_MARK ;
89
92
return 1 ;
93
+ }
90
94
91
95
if (parse_commit (candidate ) < 0 )
92
96
return 0 ;
93
97
94
- /* Otherwise recurse and mark ourselves for future traversals. */
95
- for (p = candidate -> parents ; p ; p = p -> next ) {
96
- if (contains_recurse (p -> item , want )) {
97
- candidate -> object .flags |= TMP_MARK ;
98
- return 1 ;
99
- }
100
- }
101
- candidate -> object .flags |= UNINTERESTING ;
102
- return 0 ;
98
+ return -1 ;
99
+ }
100
+
101
+ /*
102
+ * Mimicking the real stack, this stack lives on the heap, avoiding stack
103
+ * overflows.
104
+ *
105
+ * At each recursion step, the stack items points to the commits whose
106
+ * ancestors are to be inspected.
107
+ */
108
+ struct stack {
109
+ int nr , alloc ;
110
+ struct stack_entry {
111
+ struct commit * commit ;
112
+ struct commit_list * parents ;
113
+ } * stack ;
114
+ };
115
+
116
+ static void push_to_stack (struct commit * candidate , struct stack * stack )
117
+ {
118
+ int index = stack -> nr ++ ;
119
+ ALLOC_GROW (stack -> stack , stack -> nr , stack -> alloc );
120
+ stack -> stack [index ].commit = candidate ;
121
+ stack -> stack [index ].parents = candidate -> parents ;
103
122
}
104
123
105
124
static int contains (struct commit * candidate , const struct commit_list * want )
106
125
{
107
- return contains_recurse (candidate , want );
126
+ struct stack stack = { 0 , 0 , NULL };
127
+ int result = contains_test (candidate , want );
128
+
129
+ if (result >= 0 )
130
+ return result ;
131
+
132
+ push_to_stack (candidate , & stack );
133
+ while (stack .nr ) {
134
+ struct stack_entry * entry = & stack .stack [stack .nr - 1 ];
135
+ struct commit * commit = entry -> commit ;
136
+ struct commit_list * parents = entry -> parents ;
137
+
138
+ if (!parents ) {
139
+ commit -> object .flags = UNINTERESTING ;
140
+ stack .nr -- ;
141
+ }
142
+ /*
143
+ * If we just popped the stack, parents->item has been marked,
144
+ * therefore contains_test will return a meaningful 0 or 1.
145
+ */
146
+ else switch (contains_test (parents -> item , want )) {
147
+ case 1 :
148
+ commit -> object .flags |= TMP_MARK ;
149
+ stack .nr -- ;
150
+ break ;
151
+ case 0 :
152
+ entry -> parents = parents -> next ;
153
+ break ;
154
+ default :
155
+ push_to_stack (parents -> item , & stack );
156
+ break ;
157
+ }
158
+ }
159
+ free (stack .stack );
160
+ return contains_test (candidate , want );
108
161
}
109
162
110
163
static void show_tag_lines (const unsigned char * sha1 , int lines )
0 commit comments