git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: "Chris Torek via GitGitGadget" <gitgitgadget@gmail.com>
Cc: git@vger.kernel.org, Chris Torek <chris.torek@gmail.com>
Subject: Re: [PATCH 2/3] git diff: improve A...B merge-base handling
Date: Mon, 08 Jun 2020 22:40:57 -0700	[thread overview]
Message-ID: <xmqqmu5ciyom.fsf@gitster.c.googlers.com> (raw)
In-Reply-To: <f7c8f094e02406a7d0cb0c61f880e5b01fa413c4.1591661021.git.gitgitgadget@gmail.com> (Chris Torek via GitGitGadget's message of "Tue, 09 Jun 2020 00:03:40 +0000")

"Chris Torek via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +struct symdiff {
> +	struct bitmap *skip;	/* bitmap of commit indices to skip, or NULL */
> +	int warn;		/* true if there were multiple merge bases */
> +	int base, left, right;	/* index of chosen merge base and left&right */
> +};
> +
> +/*
> + * Check for symmetric-difference arguments, and if present, arrange
> + * everything we need to know to handle them correctly.
> + *
> + * For an actual symmetric diff, *symdiff is set this way:
> + *
> + *  - its skip is non-NULL and marks *all* rev->pending.objects[i]
> + *    indices that the caller should ignore (extra merge bases, of
> + *    which there might be many, and A in A...B).  Note that the
> + *    chosen merge base and right side are NOT marked.
> + *  - warn is set if there are multiple merge bases.
> + *  - base, left, and right hold the merge base and left and
> + *    right side indices, for warnings or errors.
> + *
> + * If there is no symmetric diff argument, sym->skip is NULL and
> + * sym->warn is cleared.  The remaining fields are not set.
> + *
> + * If the user provides a symmetric diff with no merge base, or
> + * more than one range, we do a usage-exit.
> + */
> +static void builtin_diff_symdiff(struct rev_info *rev, struct symdiff *sym)

The function name feels quite suboptimal.  At least I thought that
by the time a call to this function returns, we would have already
produced a symmetric diff output from its name, but apparently that
is not what is being done.  Calling it symdiff_prepare() may be a
vast improvement, perhaps.

> +{
> +	int i, lcount = 0, rcount = 0, basecount = 0;
> +	int lpos = -1, rpos = -1, basepos = -1;
> +	struct bitmap *map = NULL;
> +
> +	/*
> +	 * Use the whence fields to find merge bases and left and
> +	 * right parts of symmetric difference, so that we do not
> +	 * depend on the order that revisions are parsed.  If there
> +	 * are any revs that aren't from these sources, we have a
> +	 * "git diff C A...B" or "git diff A...B C" case.  Or we
> +	 * could even get "git diff A...B C...E", for instance.
> +	 *
> +	 * If we don't have just one merge base, we pick one
> +	 * at random.
> +	 *
> +	 * NB: REV_CMD_LEFT, REV_CMD_RIGHT are also used for A..B,
> +	 * so we must check for SYMMETRIC_LEFT too.  The two arrays
> +	 * rev->pending.objects and rev->cmdline.rev are parallel.
> +	 */
> +	for (i = 0; i < rev->cmdline.nr; i++) {
> +		struct object *obj = rev->pending.objects[i].item;
> +		switch (rev->cmdline.rev[i].whence) {
> +		case REV_CMD_MERGE_BASE:
> +			if (basepos < 0)
> +				basepos = i;
> +			basecount++;
> +			break;		/* do mark all bases */
> +		case REV_CMD_LEFT:
> +			if (obj->flags & SYMMETRIC_LEFT) {
> +				lpos = i;
> +				lcount++;
> +				break;	/* do mark A */
> +			}
> +			continue;
> +		case REV_CMD_RIGHT:
> +			rpos = i;
> +			rcount++;

Even though, unlike lcount, you allow arbitrary number of rcount,
and rpos uses "the last one wins" semantics.  Can we describe in the
comment above what use case benefits from this looseness (as opposed
to erroring out when rcount is NOT 1, like done for lcount)?

> +			continue;	/* don't mark B */
> +		default:
> +			continue;
> +		}
> +		if (map == NULL)
> +			map = bitmap_new();
> +		bitmap_set(map, i);
> +	}
> +
> +	if (lcount == 0) {	/* not a symmetric difference */
> +		bitmap_free(map);
> +		sym->warn = 0;
> +		sym->skip = NULL;
> +		return;
> +	}
> +
> +	if (lcount != 1)
> +		die(_("cannot use more than one symmetric difference"));
> +
> +	if (basecount == 0) {
> +		const char *lname = rev->pending.objects[lpos].name;
> +		const char *rname = rev->pending.objects[rpos].name;
> +		die(_("%s...%s: no merge base"), lname, rname);
> +	}
> +	bitmap_unset(map, basepos);	/* unmark the base we want */
> +	sym->base = basepos;
> +	sym->left = lpos;
> +	sym->right = rpos;
> +	sym->warn = basecount > 1;
> +	sym->skip = map;
> +}
> +
>  int cmd_diff(int argc, const char **argv, const char *prefix)
>  {
>  	int i;
> @@ -263,6 +361,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>  	struct object_array_entry *blob[2];
>  	int nongit = 0, no_index = 0;
>  	int result = 0;
> +	struct symdiff sdiff;
>  
>  	/*
>  	 * We could get N tree-ish in the rev.pending_objects list.
> @@ -382,6 +481,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>  		}
>  	}
>  
> +	builtin_diff_symdiff(&rev, &sdiff);
>  	for (i = 0; i < rev.pending.nr; i++) {
>  		struct object_array_entry *entry = &rev.pending.objects[i];
>  		struct object *obj = entry->item;
> @@ -394,8 +494,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>  			die(_("invalid object '%s' given."), name);
>  		if (obj->type == OBJ_COMMIT)
>  			obj = &get_commit_tree(((struct commit *)obj))->object;
> -
>  		if (obj->type == OBJ_TREE) {
> +			if (sdiff.skip && bitmap_get(sdiff.skip, i))
> +				continue;
>  			obj->flags |= flags;
>  			add_object_array(obj, name, &ent);
>  		} else if (obj->type == OBJ_BLOB) {
> @@ -437,24 +538,22 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>  		usage(builtin_diff_usage);
>  	else if (ent.nr == 1)
>  		result = builtin_diff_index(&rev, argc, argv);
> -	else if (ent.nr == 2)
> +	else if (ent.nr == 2) {
> +		if (sdiff.warn) {
> +			const char *lname = rev.pending.objects[sdiff.left].name;
> +			const char *rname = rev.pending.objects[sdiff.right].name;
> +			const char *basename = rev.pending.objects[sdiff.base].name;
> +			warning(_("%s...%s: multiple merge bases, using %s"),
> +				lname, rname, basename);
> +		}
>  		result = builtin_diff_tree(&rev, argc, argv,
>  					   &ent.objects[0], &ent.objects[1]);
> -	else if (ent.objects[0].item->flags & UNINTERESTING) {
> -		/*
> -		 * diff A...B where there is at least one merge base
> -		 * between A and B.  We have ent.objects[0] ==
> -		 * merge-base, ent.objects[ents-2] == A, and
> -		 * ent.objects[ents-1] == B.  Show diff between the
> -		 * base and B.  Note that we pick one merge base at
> -		 * random if there are more than one.
> -		 */
> -		result = builtin_diff_tree(&rev, argc, argv,
> -					   &ent.objects[0],
> -					   &ent.objects[ent.nr-1]);
> -	} else
> +	} else {
> +		if (sdiff.skip)
> +			usage(builtin_diff_usage);

sdiff.skip being non-NULL means symdiff_prepare() saw one A...B that
produced two ents and the fact that we have more than two ents mean
that the command line gave us other tree-ishes, e.g. "git diff A...B C"
and it is rejected here.  OK.

>  		result = builtin_diff_combined(&rev, argc, argv,
>  					       ent.objects, ent.nr);
> +	}

  reply	other threads:[~2020-06-09  5:41 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-09  0:03 [PATCH 0/3] improve git-diff documentation and A...B handling Chris Torek via GitGitGadget
2020-06-09  0:03 ` [PATCH 1/3] t/t3430: avoid undocumented git diff behavior Chris Torek via GitGitGadget
2020-06-09  5:18   ` Junio C Hamano
2020-06-09  0:03 ` [PATCH 2/3] git diff: improve A...B merge-base handling Chris Torek via GitGitGadget
2020-06-09  5:40   ` Junio C Hamano [this message]
2020-06-12 13:38   ` Philip Oakley
2020-06-12 17:06     ` Junio C Hamano
2020-06-09  0:03 ` [PATCH 3/3] Documentation: tweak git diff help slightly Chris Torek via GitGitGadget
2020-06-09  5:45   ` Junio C Hamano
2020-06-09 19:00 ` [PATCH v2 0/3] improve git-diff documentation and A...B handling Chris Torek via GitGitGadget
2020-06-09 19:00   ` [PATCH v2 1/3] t/t3430: avoid undefined git diff behavior Chris Torek via GitGitGadget
2020-06-09 19:00   ` [PATCH v2 2/3] git diff: improve A...B merge-base handling Chris Torek via GitGitGadget
2020-06-09 22:52     ` Junio C Hamano
2020-06-09 19:00   ` [PATCH v2 3/3] Documentation: tweak git diff help slightly Chris Torek via GitGitGadget
2020-06-09 23:01     ` Junio C Hamano
2020-06-11 15:15   ` [PATCH v3 0/3] improve git-diff documentation and A...B handling Chris Torek via GitGitGadget
2020-06-11 15:15     ` [PATCH v3 1/3] t/t3430: avoid undefined git diff behavior Chris Torek via GitGitGadget
2020-06-11 15:15     ` [PATCH v3 2/3] git diff: improve range handling Chris Torek via GitGitGadget
2020-06-11 15:51       ` Chris Torek
2020-06-11 15:15     ` [PATCH v3 3/3] Documentation: usage for diff combined commits Chris Torek via GitGitGadget
2020-06-12 16:19     ` [PATCH v4 0/3] improve git-diff documentation and A...B handling Chris Torek via GitGitGadget
2020-06-12 16:19       ` [PATCH v4 1/3] t/t3430: avoid undefined git diff behavior Chris Torek via GitGitGadget
2020-06-12 16:19       ` [PATCH v4 2/3] git diff: improve range handling Chris Torek via GitGitGadget
2020-06-12 18:31         ` Junio C Hamano
2020-06-12 19:25           ` Chris Torek
2020-06-12 16:20       ` [PATCH v4 3/3] Documentation: usage for diff combined commits Chris Torek via GitGitGadget

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqqmu5ciyom.fsf@gitster.c.googlers.com \
    --to=gitster@pobox.com \
    --cc=chris.torek@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitgitgadget@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).