Merge branch 'mh/notes-allow-reading-treeish'

Some "git notes" operations, e.g. "git log --notes=<note>", should
be able to read notes from any tree-ish that is shaped like a notes
tree, but the notes infrastructure required that the argument must
be a ref under refs/notes/.  Loosen it to require a valid ref only
when the operation would update the notes (in which case we must
have a place to store the updated notes tree, iow, a ref).

* mh/notes-allow-reading-treeish:
  notes: allow treeish expressions as notes ref
This commit is contained in:
Junio C Hamano 2016-01-20 11:43:21 -08:00
Родитель fc10eb5b87 ee76f92fe8
Коммит b4e8e0ed2d
7 изменённых файлов: 55 добавлений и 30 удалений

Просмотреть файл

@ -43,7 +43,7 @@ people using 80-column terminals.
commit may be copied to the output.
ifndef::git-rev-list[]
--notes[=<ref>]::
--notes[=<treeish>]::
Show the notes (see linkgit:git-notes[1]) that annotate the
commit, when showing the commit log message. This is the default
for `git log`, `git show` and `git whatchanged` commands when
@ -54,8 +54,8 @@ By default, the notes shown are from the notes refs listed in the
'core.notesRef' and 'notes.displayRef' variables (or corresponding
environment overrides). See linkgit:git-config[1] for more details.
+
With an optional '<ref>' argument, show this notes ref instead of the
default notes ref(s). The ref specifies the full refname when it begins
With an optional '<treeish>' argument, use the treeish to find the notes
to display. The treeish can specify the full refname when it begins
with `refs/notes/`; when it begins with `notes/`, `refs/` and otherwise
`refs/notes/` is prefixed to form a full name of the ref.
+
@ -71,7 +71,7 @@ being displayed. Examples: "--notes=foo" will show only notes from
"--notes --notes=foo --no-notes --notes=bar" will only show notes
from "refs/notes/bar".
--show-notes[=<ref>]::
--show-notes[=<treeish>]::
--[no-]standard-notes::
These options are deprecated. Use the above --notes/--no-notes
options instead.

Просмотреть файл

@ -286,7 +286,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
if (!c)
return 0;
} else {
init_notes(NULL, NULL, NULL, 0);
init_notes(NULL, NULL, NULL, NOTES_INIT_WRITABLE);
t = &default_notes_tree;
}
@ -329,15 +329,18 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
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;
}
@ -360,7 +363,7 @@ static int list(int argc, const char **argv, const char *prefix)
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]);
@ -420,7 +423,7 @@ static int add(int argc, const char **argv, const char *prefix)
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) {
@ -511,7 +514,7 @@ static int copy(int argc, const char **argv, const char *prefix)
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) {
@ -589,7 +592,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
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);
@ -652,7 +655,7 @@ static int show(int argc, const char **argv, const char *prefix)
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,7 +812,7 @@ static int merge(int argc, const char **argv, const char *prefix)
expand_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)) {
@ -901,7 +904,7 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
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);
@ -943,7 +946,7 @@ static int prune(int argc, const char **argv, const char *prefix)
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) );

Просмотреть файл

@ -32,14 +32,14 @@ void notes_cache_init(struct notes_cache *c, const char *name,
const char *validity)
{
struct strbuf ref = STRBUF_INIT;
int flags = 0;
int flags = NOTES_INIT_WRITABLE;
memset(c, 0, sizeof(*c));
c->validity = xstrdup(validity);
strbuf_addf(&ref, "refs/notes/%s", name);
if (!notes_cache_match_validity(ref.buf, validity))
flags = NOTES_INIT_EMPTY;
flags |= NOTES_INIT_EMPTY;
init_notes(&c->tree, ref.buf, combine_notes_overwrite, flags);
strbuf_release(&ref);
}
@ -49,7 +49,8 @@ int notes_cache_write(struct notes_cache *c)
unsigned char tree_sha1[20];
unsigned char commit_sha1[20];
if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
if (!c || !c->tree.initialized || !c->tree.update_ref ||
!*c->tree.update_ref)
return -1;
if (!c->tree.dirty)
return 0;
@ -59,8 +60,8 @@ int notes_cache_write(struct notes_cache *c)
if (commit_tree(c->validity, strlen(c->validity), tree_sha1, NULL,
commit_sha1, NULL, NULL) < 0)
return -1;
if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
0, UPDATE_REFS_QUIET_ON_ERR) < 0)
if (update_ref("update notes cache", c->tree.update_ref, commit_sha1,
NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
return -1;
return 0;

Просмотреть файл

@ -37,7 +37,7 @@ void commit_notes(struct notes_tree *t, const char *msg)
if (!t)
t = &default_notes_tree;
if (!t->initialized || !t->ref || !*t->ref)
if (!t->initialized || !t->update_ref || !*t->update_ref)
die(_("Cannot commit uninitialized/unreferenced notes tree"));
if (!t->dirty)
return; /* don't have to commit an unchanged tree */
@ -48,7 +48,7 @@ void commit_notes(struct notes_tree *t, const char *msg)
create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1);
strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
update_ref(buf.buf, t->ref, commit_sha1, NULL, 0,
update_ref(buf.buf, t->update_ref, commit_sha1, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
strbuf_release(&buf);
@ -148,7 +148,7 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
free(c);
return NULL;
}
c->trees = load_notes_trees(c->refs);
c->trees = load_notes_trees(c->refs, NOTES_INIT_WRITABLE);
string_list_clear(c->refs, 0);
free(c->refs);
return c;

11
notes.c
Просмотреть файл

@ -1011,13 +1011,16 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
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));
@ -1027,7 +1030,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
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;
@ -1035,7 +1038,7 @@ struct notes_tree **load_notes_trees(struct string_list *refs)
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;
@ -1071,7 +1074,7 @@ void init_display_notes(struct display_notes_opt *opt)
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);
}

10
notes.h
Просмотреть файл

@ -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;
@ -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
@ -276,7 +284,7 @@ void format_display_notes(const unsigned char *object_sha1,
* 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'.

Просмотреть файл

@ -83,6 +83,16 @@ test_expect_success 'edit existing notes' '
test_must_fail git notes show HEAD^
'
test_expect_success 'show notes from treeish' '
test "b3" = "$(git notes --ref commits^{tree} show)" &&
test "b4" = "$(git notes --ref commits@{1} show)"
'
test_expect_success 'cannot edit notes from non-ref' '
test_must_fail git notes --ref commits^{tree} edit &&
test_must_fail git notes --ref commits@{1} edit
'
test_expect_success 'cannot "git notes add -m" where notes already exists' '
test_must_fail git notes add -m "b2" &&
test_path_is_missing .git/NOTES_EDITMSG &&