git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Johan Herland <johan@herland.net>
To: Jonathan Nieder <jrnieder@gmail.com>
Cc: git@vger.kernel.org, bebarino@gmail.com, gitster@pobox.com
Subject: Re: [PATCH 08/18] git notes merge: Initial implementation handling trivial merges only
Date: Sat, 9 Oct 2010 01:55:19 +0200	[thread overview]
Message-ID: <201010090155.20858.johan@herland.net> (raw)
In-Reply-To: <20101007062433.GF2285@burratino>

On Thursday 7. October 2010 08.24.33 Jonathan Nieder wrote:
> Johan Herland wrote:
> > This initial implementation of 'git notes merge' only handles the trivial
> > merge cases (i.e. where the merge is either a no-op, or a fast-forward).
> 
> [...]
> 
> This reminds me: it would be nice if non-builtin scripts could use
> 
> 	git notes --get-notes-ref $refarg
> 
> to learn the configured notes ref.  In other words, shouldn't the
> default_notes_ref() exposed in patch 2 also be exposed to scripted
> callers?

I agree, but I'd like to name it 'git notes get-ref' instead, to stay
consistent with the current subcommand (instead of option) design.

> If someone else doesn't get around to it, I can mock
> something up in the next few days.

There is a patch implementing 'git notes get-ref' in the next iteration
of this series. :)

> > +++ b/notes-merge.h
> > @@ -0,0 +1,30 @@
> > +#ifndef NOTES_MERGE_H
> > +#define NOTES_MERGE_H
> > +
> > +struct notes_merge_options {
> > +	const char *local_ref;
> > +	const char *remote_ref;
> > +	int verbosity;
> > +};
> > +
> > +void init_notes_merge_options(struct notes_merge_options *o);
> 
> [...]
> 
> > +int notes_merge(struct notes_merge_options *o,
> > +		unsigned char *result_sha1);
> 
> So the command is usable as-is from other builtins.  Nice.

Yep, that's the idea. notes-merge.h is really only an extension of
notes.h for providing the merge-related functionality.

> > +++ b/notes-merge.c
> > @@ -0,0 +1,103 @@
> 
> [...]
> 
> > +void init_notes_merge_options(struct notes_merge_options *o)
> > +{
> > +	memset(o, 0, sizeof(struct notes_merge_options));
> > +	o->verbosity = 2;
> > +}
> > +
> > +static int show(struct notes_merge_options *o, int v)
> > +{
> > +	return (o->verbosity >= v) || o->verbosity >= 5;
> > +}
> 
> Should the verbosities be of enum type?
> 
> 	enum notes_merge_verbosity {
> 		DEFAULT_VERBOSITY = 2,
> 		MAX_VERBOSITY = 5,
> 		etc
> 	};

Maybe. I've added these constants and used them where it made sense
(to me) in the next iteration.

> > +
> > +#define OUTPUT(o, v, ...) \
> > +	do { if (show((o), (v))) { printf(__VA_ARGS__); puts(""); } } while (0)
> 
> I would find it easier to read
> 
> 	if (o->verbosity >= DEFAULT_VERBOSITY)
> 		fprintf(stderr, ...)
> 
> unless there are going to be a huge number of messages.

The current version is modeled on the show() and output() functions in
merge-recursive.c. I think that works better in this situation.
Or maybe you have a better solution for merge-recursive.c as well?

> > +
> > +int notes_merge(struct notes_merge_options *o,
> > +		unsigned char *result_sha1)
> > +{
> > +	unsigned char local_sha1[20], remote_sha1[20];
> > +	struct commit *local, *remote;
> > +	struct commit_list *bases = NULL;
> > +	const unsigned char *base_sha1;
> > +	int result = 0;
> > +
> > +	hashclr(result_sha1);
> > +
> > +	OUTPUT(o, 5, "notes_merge(o->local_ref = %s, o->remote_ref = %s)",
> > +	       o->local_ref, o->remote_ref);
> 
> Would trace_printf be a good fit for messages like this one?  If not,
> any idea about how it could be made to fit some day?
> 
> (It is especially nice to be able to direct the trace somewhere other
> than stdout and stderr when running tests.)

Agreed. The OUTPUT(o, 5, ...) instances should use trace_printf()
instead. Fixed in the next iteration.

> > +
> > +	if (!o->local_ref || get_sha1(o->local_ref, local_sha1)) {
> > +		/* empty notes ref => assume empty notes tree */
> 
> Can an empty ref be distinguished from a missing ref?  It would be
> nice to error out when breakage is detected.

I'm not sure when you think we should (or shouldn't) error out. FWIW,
I've modeled the current code on what 'git merge' does. See [1] for
more details.

> > +	/* Find merge bases */
> > +	bases = get_merge_bases(local, remote, 1);
> > +	if (!bases) {
> > +		base_sha1 = null_sha1;
> > +		OUTPUT(o, 4, "No merge base found; doing history-less merge");
> > +	} else if (!bases->next) {
> > +		base_sha1 = bases->item->object.sha1;
> > +		OUTPUT(o, 4, "One merge base found (%.7s)",
> > +		       sha1_to_hex(base_sha1));
> > +	} else {
> > +		/* TODO: How to handle multiple merge-bases? */
> 
> With a recursive merge of the ancestors, of course. :)
> 
> The difficult part is what to do when a merge of ancestors results in
> conflicts.

Exactly. I feel notes merge conflicts are confusing enough to the
average user, and I'd rather not expose them to resolving conflicts in
_recursive_ notes merges...

I guess this becomes a discussion of whether we should model notes
merges on the 'resolve' merge strategy or the 'recursive' merge
strategy. Without having studied each strategy in-depth, I don't know
how much "better" 'recursive' is than 'resolve', especially not from
the POV of notes merges. Are there features of 'recursive' that are
useful to notes merges?

(All I know of the effectual differences between 'resolve' and
'recursive' is the hand waving on the 'git merge' man page:
  "...has been reported to result in fewer merge conflicts without
   causing mis-merges by tests done on actual merge commits taken from
   Linux 2.6 kernel development history.")

In conclusion, if someone can show that's it's worth implementing
recursive notes merging, then I'll gladly do it, but until then I'll
stick with the simplicity of using the first merge base.

> > +++ b/builtin/notes.c
> > @@ -772,6 +779,50 @@ static int show(int argc, const char **argv, const
> > +static int merge(int argc, const char **argv, const char *prefix)
> > +{
> > [...]
> > +	o.verbosity = verbosity + 2; // default verbosity level is 2
> 
> Maybe
> 
> 	o.verbosity += verbosity;
> 
> or
> 
> 	o.verbosity = DEFAULT_NOTES_MERGE_VERBOSITY + verbosity;
> 
> to allow the default verbosity to be set in one place?

Agreed. Fixed in the next iteration.

> > +	result = notes_merge(&o, result_sha1);
> > +
> > +	strbuf_addf(&msg, "notes: Merged notes from %s into %s",
> > +		    remote_ref.buf, default_notes_ref());
> > +	if (result == 0) { /* Merge resulted (trivially) in result_sha1 */
> > +		/* Update default notes ref with new commit */
> > +		update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
> > +			   0, DIE_ON_ERR);
> > +	} else {
> > +		/* TODO: */
> > +		die("'git notes merge' cannot yet handle non-trivial merges!");
> 
> Mm.  In the long run, will (result != 0) mean "merge conflict"?

Almost. If you look at the notes_merge() docs in notes-merge.h by the
end of this series, you'll see the following return values:

0: Merge trivially succeeded in an existing commit (e.g. fast-forward).

1: Merge successfully completes a merge commit (i.e. no conflicts).

-1: Merge results is conflicts.

> Thanks for a pleasant read.

Thanks for reading! :)


...Johan


[1]: Let's analyze the different cases here:

1. local_ref ("our" side of the merge) doesn't exist
(i.e. an unborn notes ref):

In this situation 'git merge' simply creates local_ref to point at
remote_ref. 'git notes merge' does the same.


2. local_ref is empty (.git/refs/notes/$local_ref is an empty file):

'git merge' fails with
  error: unable to resolve reference HEAD: No such file or directory
  fatal: Cannot lock the ref 'HEAD'.

'git notes merge' fails with
  error: unable to resolve reference refs/notes/$local_ref: No such file or directory
  fatal: Cannot lock the ref 'refs/notes/$local_ref'.

3. local_ref refers to a non-existing commit:

'git merge' fails with
  error: Could not read $non_existing_sha1
  error: Trying to write ref ORIG_HEAD with nonexistant object $non_existing_sha1
  fatal: Cannot update the ref 'ORIG_HEAD'.
  fatal: bad object HEAD

'git notes merge' fails with
  fatal: Failed to read notes tree referenced by refs/notes/$local_ref ($non_existing_sha1)

4. local_ref refers to a non-commit (tree or blob):

'git merge' fails with
  error: Object $tree_sha1 is a tree, not a commit
  error: Object $tree_sha1 is a tree, not a commit
  Segmentation fault (someone might want to look into this...)

'git notes merge' fails with
  error: Object $tree_sha1 is a tree, not a commit
  fatal: Could not parse commit 'refs/notes/$local_ref.

Looking at remote_ref instead:

5. remote_ref ("their" side of the merge) doesn't exist
(i.e. an unborn notes ref):

'git merge' fails with:
  fatal: '$remote_ref' does not point to a commit

'git notes merge' fails with:
  fatal: Could not parse commit 'refs/notes/$remote_ref'.

6. remote_ref is empty (.git/refs/notes/$remote_ref is an empty file):

same as #5

7. remote_ref refers to a non-existing commit:

same as #5

8. remote_ref refers to a non-commit (tree or blob):

'git merge' fails with
  error: $remote_ref: expected commit type, but the object dereferences to tree type
  fatal: '$remote_ref' does not point to a commit

'git notes merge' fails with
  error: Object $tree_sha1 is a tree, not a commit
  fatal: Could not parse commit '$remote_ref'.

I believe all these outcomes make sense.

-- 
Johan Herland, <johan@herland.net>
www.herland.net

  reply	other threads:[~2010-10-09  0:00 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-09-29  0:23 [PATCH 00/18] git notes merge Johan Herland
2010-09-29  0:23 ` [PATCH 01/18] (trivial) notes.h: Minor documentation fixes to copy_notes() Johan Herland
2010-10-05 14:55   ` Jonathan Nieder
2010-10-05 15:22     ` Johan Herland
2010-10-05 15:26       ` Jonathan Nieder
2010-09-29  0:23 ` [PATCH 02/18] notes.h: Make default_notes_ref() available in notes API Johan Herland
2010-09-29  0:23 ` [PATCH 03/18] notes.h/c: Clarify the handling of notes objects that are == null_sha1 Johan Herland
2010-10-05 15:21   ` Jonathan Nieder
2010-10-05 22:30     ` Johan Herland
2010-09-29  0:23 ` [PATCH 04/18] notes.h/c: Propagate combine_notes_fn return value to add_note() and beyond Johan Herland
2010-10-05 15:38   ` Jonathan Nieder
2010-10-06 19:40     ` Johan Herland
2010-09-29  0:23 ` [PATCH 05/18] (trivial) t3303: Indent with tabs instead of spaces for consistency Johan Herland
2010-09-29  0:23 ` [PATCH 06/18] notes.c: Use two newlines (instead of one) when concatenating notes Johan Herland
2010-09-29  0:23 ` [PATCH 07/18] builtin/notes.c: Split notes ref DWIMmery into a separate function Johan Herland
2010-10-05 15:50   ` Jonathan Nieder
2010-10-07 13:56     ` Johan Herland
2010-09-29  0:23 ` [PATCH 08/18] git notes merge: Initial implementation handling trivial merges only Johan Herland
2010-10-07  4:37   ` Test script style (Re: [PATCH 08/18] git notes merge: Initial implementation handling trivial merges only) Jonathan Nieder
2010-10-07  4:57     ` Junio C Hamano
2010-10-07  6:24   ` [PATCH 08/18] git notes merge: Initial implementation handling trivial merges only Jonathan Nieder
2010-10-08 23:55     ` Johan Herland [this message]
2010-10-09 17:29       ` Jonathan Nieder
2010-10-21  2:12         ` Johan Herland
2010-09-29  0:23 ` [PATCH 09/18] builtin/notes.c: Refactor creation of notes commits Johan Herland
2010-09-29  0:23 ` [PATCH 10/18] git notes merge: Handle real, non-conflicting notes merges Johan Herland
2010-09-29 16:20   ` Ævar Arnfjörð Bjarmason
2010-09-29 23:25     ` Johan Herland
2010-09-29  0:23 ` [PATCH 11/18] git notes merge: Add automatic conflict resolvers (ours, theirs, union) Johan Herland
2010-10-02  9:14   ` Stephen Boyd
2010-10-04 15:10     ` Johan Herland
2010-09-29  0:23 ` [PATCH 12/18] Documentation: Preliminary docs on 'git notes merge' Johan Herland
2010-10-02  8:55   ` Stephen Boyd
2010-10-04 15:15     ` Johan Herland
2010-09-29  0:23 ` [PATCH 13/18] git notes merge: Manual conflict resolution, part 1/2 Johan Herland
2010-09-29  0:23 ` [PATCH 14/18] git notes merge: Manual conflict resolution, part 2/2 Johan Herland
2010-09-29 16:19   ` Ævar Arnfjörð Bjarmason
2010-09-29 23:37     ` Johan Herland
2010-09-29  0:23 ` [PATCH 15/18] git notes merge: List conflicting notes in notes merge commit message Johan Herland
2010-09-29  0:23 ` [PATCH 16/18] git notes merge: --commit should fail if underlying notes ref has moved Johan Herland
2010-09-29  0:23 ` [PATCH 17/18] git notes merge: Add another auto-resolving strategy: "cat_sort_uniq" Johan Herland
2010-09-29  0:23 ` [PATCH 18/18] git notes merge: Add testcases for merging notes trees at different fanouts Johan Herland
2010-09-29 14:56 ` [PATCH 00/18] git notes merge Sverre Rabbelier
2010-09-29 15:16   ` Johan Herland
2010-09-29 15:20     ` Sverre Rabbelier
2010-09-29 16:04       ` Johan Herland

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=201010090155.20858.johan@herland.net \
    --to=johan@herland.net \
    --cc=bebarino@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=jrnieder@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).