@@ -757,7 +757,8 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
757757
758758/*
759759 * Test whether the candidate is contained in the list.
760- * Do not recurse to find out, though, but return -1 if inconclusive.
760+ * Do not recurse to find out, though, but return CONTAINS_UNKNOWN if
761+ * inconclusive.
761762 */
762763static enum contains_result contains_test (struct commit * candidate ,
763764 const struct commit_list * want ,
@@ -793,7 +794,7 @@ static void push_to_contains_stack(struct commit *candidate, struct contains_sta
793794}
794795
795796static enum contains_result contains_tag_algo (struct commit * candidate ,
796- const struct commit_list * want ,
797+ struct commit_list * want ,
797798 struct contains_cache * cache )
798799{
799800 struct contains_stack contains_stack = { 0 , 0 , NULL };
@@ -814,6 +815,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
814815 if (result != CONTAINS_UNKNOWN )
815816 return result ;
816817
818+ * contains_cache_at (cache , candidate ) = CONTAINS_IN_PROGRESS ;
817819 push_to_contains_stack (candidate , & contains_stack );
818820 while (contains_stack .nr ) {
819821 struct contains_stack_entry * entry = & contains_stack .contains_stack [contains_stack .nr - 1 ];
@@ -825,8 +827,8 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
825827 contains_stack .nr -- ;
826828 }
827829 /*
828- * If we just popped the stack, parents->item has been marked,
829- * therefore contains_test will return a meaningful yes/no .
830+ * A parent may have just been popped and marked, or may still
831+ * be active when replacement refs create a cycle .
830832 */
831833 else switch (contains_test (parents -> item , want , cache , cutoff )) {
832834 case CONTAINS_YES :
@@ -836,21 +838,50 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
836838 case CONTAINS_NO :
837839 entry -> parents = parents -> next ;
838840 break ;
841+ case CONTAINS_IN_PROGRESS :
842+ /*
843+ * Partial negative answers are not safe across a cycle.
844+ * Discard them and use the cycle-safe reachability walk.
845+ */
846+ goto cycle ;
839847 case CONTAINS_UNKNOWN :
848+ * contains_cache_at (cache , parents -> item ) =
849+ CONTAINS_IN_PROGRESS ;
840850 push_to_contains_stack (parents -> item , & contains_stack );
841851 break ;
842852 }
843853 }
844854 free (contains_stack .contains_stack );
845855 return contains_test (candidate , want , cache , cutoff );
856+
857+ cycle :
858+ free (contains_stack .contains_stack );
859+ clear_contains_cache (cache );
860+ init_contains_cache (cache );
861+
862+ result = repo_is_descendant_of (the_repository , candidate , want );
863+ if (result < 0 )
864+ exit (128 );
865+ * contains_cache_at (cache , candidate ) =
866+ result ? CONTAINS_YES : CONTAINS_NO ;
867+ return result ? CONTAINS_YES : CONTAINS_NO ;
846868}
847869
848870int commit_contains (struct ref_filter * filter , struct commit * commit ,
849871 struct commit_list * list , struct contains_cache * cache )
850872{
851- if (filter -> with_commit_tag_algo )
873+ int result ;
874+
875+ if (!list )
876+ return 1 ;
877+ if (filter -> with_commit_tag_algo ||
878+ generation_numbers_enabled (the_repository ))
852879 return contains_tag_algo (commit , list , cache ) == CONTAINS_YES ;
853- return repo_is_descendant_of (the_repository , commit , list );
880+
881+ result = repo_is_descendant_of (the_repository , commit , list );
882+ if (result < 0 )
883+ exit (128 );
884+ return result ;
854885}
855886
856887int can_all_from_reach_with_flag (struct object_array * from ,
0 commit comments