Merge branch 'jk/notes-merge-from-anywhere'
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Feb 2016 22:15:59 +0000 (14:15 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Feb 2016 22:15:59 +0000 (14:15 -0800)
"git notes merge" used to limit the source of the merged notes tree
to somewhere under refs/notes/ hierarchy, which was too limiting
when inventing a workflow to exchange notes with remote
repositories using remote-tracking notes trees (located in e.g.
refs/remote-notes/ or somesuch).

* jk/notes-merge-from-anywhere:
notes: allow merging from arbitrary references

1  2 
builtin/notes.c
notes.c
notes.h
diff --combined builtin/notes.c
index 9d54934d6c4ccf55ed24304a3da5a6fd0f0206ca,b16b8b56a0a2291f1a27be8e025ab3773197d77c..ed6f2222f4bf9c54535a7a23971c7749ca8d05d5
@@@ -286,11 -286,11 +286,11 @@@ static int notes_copy_from_stdin(int fo
                if (!c)
                        return 0;
        } else {
 -              init_notes(NULL, NULL, NULL, 0);
 +              init_notes(NULL, NULL, NULL, NOTES_INIT_WRITABLE);
                t = &default_notes_tree;
        }
  
 -      while (strbuf_getline(&buf, stdin, '\n') != EOF) {
 +      while (strbuf_getline_lf(&buf, stdin) != EOF) {
                unsigned char from_obj[20], to_obj[20];
                struct strbuf **split;
                int err;
        return ret;
  }
  
 -static struct notes_tree *init_notes_check(const char *subcommand)
 +static struct notes_tree *init_notes_check(const char *subcommand,
 +                                         int flags)
  {
        struct notes_tree *t;
 -      init_notes(NULL, NULL, NULL, 0);
 +      const char *ref;
 +      init_notes(NULL, NULL, NULL, flags);
        t = &default_notes_tree;
  
 -      if (!starts_with(t->ref, "refs/notes/"))
 +      ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref;
 +      if (!starts_with(ref, "refs/notes/"))
                die("Refusing to %s notes in %s (outside of refs/notes/)",
 -                  subcommand, t->ref);
 +                  subcommand, ref);
        return t;
  }
  
@@@ -363,7 -360,7 +363,7 @@@ static int list(int argc, const char **
                usage_with_options(git_notes_list_usage, options);
        }
  
 -      t = init_notes_check("list");
 +      t = init_notes_check("list", 0);
        if (argc) {
                if (get_sha1(argv[0], object))
                        die(_("Failed to resolve '%s' as a valid ref."), argv[0]);
@@@ -423,7 -420,7 +423,7 @@@ static int add(int argc, const char **a
        if (get_sha1(object_ref, object))
                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
  
 -      t = init_notes_check("add");
 +      t = init_notes_check("add", NOTES_INIT_WRITABLE);
        note = get_note(t, object);
  
        if (note) {
@@@ -514,7 -511,7 +514,7 @@@ static int copy(int argc, const char **
        if (get_sha1(object_ref, object))
                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
  
 -      t = init_notes_check("copy");
 +      t = init_notes_check("copy", NOTES_INIT_WRITABLE);
        note = get_note(t, object);
  
        if (note) {
@@@ -592,7 -589,7 +592,7 @@@ static int append_edit(int argc, const 
        if (get_sha1(object_ref, object))
                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
  
 -      t = init_notes_check(argv[0]);
 +      t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
        note = get_note(t, object);
  
        prepare_note_data(object, &d, edit ? note : NULL);
@@@ -655,7 -652,7 +655,7 @@@ static int show(int argc, const char **
        if (get_sha1(object_ref, object))
                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
  
 -      t = init_notes_check("show");
 +      t = init_notes_check("show", 0);
        note = get_note(t, object);
  
        if (!note)
@@@ -809,10 -806,10 +809,10 @@@ static int merge(int argc, const char *
  
        o.local_ref = default_notes_ref();
        strbuf_addstr(&remote_ref, argv[0]);
-       expand_notes_ref(&remote_ref);
+       expand_loose_notes_ref(&remote_ref);
        o.remote_ref = remote_ref.buf;
  
 -      t = init_notes_check("merge");
 +      t = init_notes_check("merge", NOTES_INIT_WRITABLE);
  
        if (strategy) {
                if (parse_notes_merge_strategy(strategy, &o.strategy)) {
@@@ -904,7 -901,7 +904,7 @@@ static int remove_cmd(int argc, const c
        argc = parse_options(argc, argv, prefix, options,
                             git_notes_remove_usage, 0);
  
 -      t = init_notes_check("remove");
 +      t = init_notes_check("remove", NOTES_INIT_WRITABLE);
  
        if (!argc && !from_stdin) {
                retval = remove_one_note(t, "HEAD", flag);
@@@ -946,7 -943,7 +946,7 @@@ static int prune(int argc, const char *
                usage_with_options(git_notes_prune_usage, options);
        }
  
 -      t = init_notes_check("prune");
 +      t = init_notes_check("prune", NOTES_INIT_WRITABLE);
  
        prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
                (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
diff --combined notes.c
index 358e2fdb74eb8cc92f16b33891a9a5f0a508de9c,086cc483e518573827490a789a77f2adfa45e945..c1e503559076398b1edc5fabbaab8f3cac5ac7d6
+++ b/notes.c
@@@ -1011,16 -1011,13 +1011,16 @@@ void init_notes(struct notes_tree *t, c
        t->first_non_note = NULL;
        t->prev_non_note = NULL;
        t->ref = xstrdup_or_null(notes_ref);
 +      t->update_ref = (flags & NOTES_INIT_WRITABLE) ? t->ref : NULL;
        t->combine_notes = combine_notes;
        t->initialized = 1;
        t->dirty = 0;
  
        if (flags & NOTES_INIT_EMPTY || !notes_ref ||
 -          read_ref(notes_ref, object_sha1))
 +          get_sha1_treeish(notes_ref, object_sha1))
                return;
 +      if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, object_sha1))
 +              die("Cannot use notes ref %s", notes_ref);
        if (get_tree_entry(object_sha1, "", sha1, &mode))
                die("Failed to read notes tree referenced by %s (%s)",
                    notes_ref, sha1_to_hex(object_sha1));
        load_subtree(t, &root_tree, t->root, 0);
  }
  
 -struct notes_tree **load_notes_trees(struct string_list *refs)
 +struct notes_tree **load_notes_trees(struct string_list *refs, int flags)
  {
        struct string_list_item *item;
        int counter = 0;
        trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
        for_each_string_list_item(item, refs) {
                struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
 -              init_notes(t, item->string, combine_notes_ignore, 0);
 +              init_notes(t, item->string, combine_notes_ignore, flags);
                trees[counter++] = t;
        }
        trees[counter] = NULL;
@@@ -1074,7 -1071,7 +1074,7 @@@ void init_display_notes(struct display_
                                                     item->string);
        }
  
 -      display_notes_trees = load_notes_trees(&display_notes_refs);
 +      display_notes_trees = load_notes_trees(&display_notes_refs, 0);
        string_list_clear(&display_notes_refs, 0);
  }
  
@@@ -1306,3 -1303,13 +1306,13 @@@ void expand_notes_ref(struct strbuf *sb
        else
                strbuf_insert(sb, 0, "refs/notes/", 11);
  }
+ void expand_loose_notes_ref(struct strbuf *sb)
+ {
+       unsigned char object[20];
+       if (get_sha1(sb->buf, object)) {
+               /* fallback to expand_notes_ref */
+               expand_notes_ref(sb);
+       }
+ }
diff --combined notes.h
index e5d67fd3754aab644791f2062d3789b38cf6bd38,431f14378817b429e1042925bd639e17f339eb33..5345642cfd1c36f3935cb4a7e38ab32f6992b383
+++ b/notes.h
@@@ -44,7 -44,6 +44,7 @@@ extern struct notes_tree 
        struct int_node *root;
        struct non_note *first_non_note, *prev_non_note;
        char *ref;
 +      char *update_ref;
        combine_notes_fn combine_notes;
        int initialized;
        int dirty;
@@@ -72,13 -71,6 +72,13 @@@ const char *default_notes_ref(void)
   */
  #define NOTES_INIT_EMPTY 1
  
 +/*
 + * By default, the notes tree is only readable, and the notes ref can be
 + * any treeish. The notes tree can however be made writable with this flag,
 + * in which case only strict ref names can be used.
 + */
 +#define NOTES_INIT_WRITABLE 2
 +
  /*
   * Initialize the given notes_tree with the notes tree structure at the given
   * ref. If given ref is NULL, the value of the $GIT_NOTES_REF environment
@@@ -284,7 -276,7 +284,7 @@@ void format_display_notes(const unsigne
   * Load the notes tree from each ref listed in 'refs'.  The output is
   * an array of notes_tree*, terminated by a NULL.
   */
 -struct notes_tree **load_notes_trees(struct string_list *refs);
 +struct notes_tree **load_notes_trees(struct string_list *refs, int flags);
  
  /*
   * Add all refs that match 'glob' to the 'list'.
@@@ -302,4 -294,11 +302,11 @@@ void string_list_add_refs_from_colon_se
  /* Expand inplace a note ref like "foo" or "notes/foo" into "refs/notes/foo" */
  void expand_notes_ref(struct strbuf *sb);
  
+ /*
+  * Similar to expand_notes_ref, but will check whether the ref can be located
+  * via get_sha1 first, and only falls back to expand_notes_ref in the case
+  * where get_sha1 fails.
+  */
+ void expand_loose_notes_ref(struct strbuf *sb);
  #endif