git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v6] Implement --first-parent for git rev-list --bisect

From
Junio C Hamano <gitster@pobox.com>
Date
Aug 28, 2018, 16:45 UTC
Message-ID
<xmqqlg8qh22q.fsf@gitster-ct.c.googlers.com>
In-Reply-To
<20180828123234.44582-1-tiagonbotelho@hotmail.com>
Tiago Botelho <tiagonbotelho@gmail.com> writes:
Show 25 quoted lines
> diff --git a/bisect.c b/bisect.c
> index 4eafc8262..cb80f29c5 100644
> --- a/bisect.c
> +++ b/bisect.c
> @@ -82,15 +82,16 @@ static inline void weight_set(struct commit_list *elem, int weight)
>  	*((int*)(elem->item->util)) = weight;
>  }
>  
> -static int count_interesting_parents(struct commit *commit)
> +static int count_interesting_parents(struct commit *commit, unsigned bisect_flags)
>  {
>  	struct commit_list *p;
>  	int count;
>  
>  	for (count = 0, p = commit->parents; p; p = p->next) {
> -		if (p->item->object.flags & UNINTERESTING)
> -			continue;
> -		count++;
> +		if (!(p->item->object.flags & UNINTERESTING))
> +		    count++;
> +		if (bisect_flags & BISECT_FIRST_PARENT)
> +			break;
>  	}
>  	return count;
>  }

We either say 0 ("the first parent is uninteresting") or 1 ("it is") under BISECT_FIRST_PARENT mode. OK.

Show 14 quoted lines
> @@ -117,10 +118,10 @@ static inline int halfway(struct commit_list *p, int nr)
>  }
>  
>  #if !DEBUG_BISECT
> -#define show_list(a,b,c,d) do { ; } while (0)
> +#define show_list(a,b,c,d,e) do { ; } while (0)
>  #else
>  static void show_list(const char *debug, int counted, int nr,
> -		      struct commit_list *list)
> +		      struct commit_list *list, unsigned bisect_flags)
>  {
>  	struct commit_list *p;
>  
> @@ -146,10 +147,14 @@ static void show_list(const char *debug, int counted, int nr,

An unrelated tangent, but I think I just spotted a bug in the existing code on the line immediately before this hunk, which reads

		if (commit->util)
			fprintf(stderr, "%3d", weight(p));

I think this was a bug introduced at bb408ac9 ("bisect.c: use commit-slab for commit weight instead of commit->util", 2018-05-19) where the internal implementation of weight() was changed not to touch commit->util but instead to use a separate commit-slab storage

Looking at the code before that conversion, it seems that we were using ->util to store a pointer to an integer, so we had the ability to differenciate non-negative weight (i.e. weight already computed for the commit), negative weight (i.e. not computed yet, but will be), and commits to which the concept of weight is not applicable. When we went to the commit-slab with the change, we lost the ability to represent the third case. I am offhand not sure what the best remedy would be. Perhaps stuff a so-far unused value like -3 to the weight() and use weight(p) == -3 instead of the old !commit->util or something like that?

(Duy CC'ed to help checking my sanity on this point).

In any case, this is an existing bug in a debug helper, and the focus of this patch is not about fixing that bug, so you can and should leave it as-is, until this patch successfully adds the "bisection following only the first parent" feature.

Show 12 quoted lines
>  		else
>  			fprintf(stderr, "---");
>  		fprintf(stderr, " %.*s", 8, oid_to_hex(&commit->object.oid));
> -		for (pp = commit->parents; pp; pp = pp->next)
> +		for (pp = commit->parents; pp; pp = pp->next) {
>  			fprintf(stderr, " %.*s", 8,
>  				oid_to_hex(&pp->item->object.oid));
>  
> +			if (bisect_flags & BISECT_FIRST_PARENT)
> +				break;
> +		}
> +

I am not sure if we want to do this. This is a debugging aid that allows us to peek into the internal state of the commit history graph being worked on; don't we rather want to make sure that tips of merged side branches are not participating (e.g. commit->util that records "weight" is used differently between the first and other parent commits)?

Show 19 quoted lines
>  		subject_len = find_commit_subject(buf, &subject_start);
>  		if (subject_len)
>  			fprintf(stderr, " %.*s", subject_len, subject_start);
> @@ -267,13 +272,13 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
>  		unsigned flags = commit->object.flags;
>  
>  		p->item->util = &weights[n++];
> -		switch (count_interesting_parents(commit)) {
> +		switch (count_interesting_parents(commit, bisect_flags)) {
>  		case 0:
>  			if (!(flags & TREESAME)) {
>  				weight_set(p, 1);
>  				counted++;
>  				show_list("bisection 2 count one",
> -					  counted, nr, list);
> +					  counted, nr, list, bisect_flags);
>  			}
>  			/*
>  			 * otherwise, it is known not to reach any

Because count_interesting_parents() returns either 0 or 1 under BISECT_FIRST_PARENT mode, we will never set -2 in this switch() statement. We either paint the bottom boundary with the correct weight, or -1 (i.e. "its weight is one plus its first parent's weight" marker). OK.

Show 15 quoted lines
> @@ -289,7 +294,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
>  		}
>  	}
>  
> -	show_list("bisection 2 initialize", counted, nr, list);
> +	show_list("bisection 2 initialize", counted, nr, list, bisect_flags);
>  
>  	/*
>  	 * If you have only one parent in the resulting set
> @@ -319,7 +324,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
>  		counted++;
>  	}
>  
> -	show_list("bisection 2 count_distance", counted, nr, list);
> +	show_list("bisection 2 count_distance", counted, nr, list, bisect_flags);

The loop before this part (not shown) is about counting weight for merges the hard way, which does not apply to us under the new BISECT_FIRST_PARENT mode. IOW, "initialize" and "count_distance" should show exactly the same output from show_list().

Show 5 quoted lines
>  	while (counted < nr) {
>  		for (p = list; p; p = p->next) {
> @@ -329,6 +334,11 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
>  			if (0 <= weight(p))
>  				continue;

If we know the weight of 'p' already, then we are already done with it. In the normal code, that means we are done with all the merges, so the loop immediately below deals only with a single strand of pearls. However, under BISECT_FIRST_PARENT mode, that is different. Let's see.

Show 6 quoted lines
>  			for (q = p->item->parents; q; q = q->next) {
> +				if ((bisect_flags & BISECT_FIRST_PARENT)) {
> +					if (weight(q) < 0)
> +						q = NULL;
> +					break;
> +				}

Under first-parent mode, we uncondtionally break out of the loop after inspecting the first parent, even if it is a merge. The code after this loop (outside the context) says

			if (!q)
				continue;

which is to say "we are not ready to set the weight of p based on the weight of q, because the latter is unknown". So that is done by setting q to NULL, which makes sort of sense.

>  				if (q->item->object.flags & UNINTERESTING)
>  					continue;
> ...
>  				if (0 <= weight(q))

Post context of this hunk is "break;", meaning that if we find an interesting parent

What the above original tells us is that when we are doing a full bisection, we do not look at weight of uninteresting parent and continue. In that case we know p is not a merge so we will exit this for() loop with q set to NULL.

Shouldn't we keep that property even under BISECT_FIRST_PARENT mode? What I am wondering is the block added to the top of this for() loop.

Shouldn't it be more like this?
                                if ((bisect_flags & BISECT_FIRST_PARENT)) {
					if ((q->item->object.flags & UNINTERESTING) ||
					    (weight(q) < 0))
						q = NULL;
					break;
				}

I do not even recall if weight(q) is defined for an uninteresting q offhand; your patch requires weight(q) is defined and is negative for such a 'q' to be correct. Code would be more trivially correct with an explicit check for UNINTERESTING, I would think.

In any case, after finding a parent (or not finding, signalled by being NULL) whose weight is either the same or one less than our weight, we leave the above for() loop and then ...

Show 9 quoted lines
> @@ -346,7 +356,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
>  				weight_set(p, weight(q)+1);
>  				counted++;
>  				show_list("bisection 2 count one",
> -					  counted, nr, list);
> +					  counted, nr, list, bisect_flags);
>  			}
>  			else
>  				weight_set(p, weight(q));
... we set our weight based on its weight.  OK.
The remainder of the change looked OK (I didn't look at the the test).
Thanks.
Previous: Junio C HamanoNext: Duy Nguyen
Message 6 of 12 in “Implement --first-parent for git rev-list --bisect”
  1. Implement --first-parent for git rev-list --bisectTiago Botelho, Aug 28, 2018
  2. Johannes SchindelinAug 28, 2018
  3. Junio C HamanoAug 28, 2018
  4. Junio C HamanoAug 28, 2018
  5. Junio C HamanoAug 28, 2018
  6. Junio C HamanoAug 28, 2018
  7. Duy NguyenSep 2, 2018
  8. bisect.c: make show_list() build againNguyễn Thái Ngọc Duy, Sep 2, 2018
  9. Christian CouderSep 2, 2018
  10. Duy NguyenSep 3, 2018
  11. Christian CouderSep 4, 2018
  12. Junio C HamanoSep 4, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.