Skip to content

Commit

Permalink
Support showing notes from more than one notes tree
Browse files Browse the repository at this point in the history
With this patch, you can set notes.displayRef to a glob that points at
your favourite notes refs, e.g.,

[notes]
	displayRef = refs/notes/*

Then git-log and friends will show notes from all trees.

Thanks to Junio C Hamano for lots of feedback, which greatly
influenced the design of the entire series and this commit in
particular.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
Acked-by: Johan Herland <johan@herland.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
trast authored and gitster committed Mar 13, 2010
1 parent 6ceeaee commit 894a9d3
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 30 deletions.
23 changes: 21 additions & 2 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,12 @@ check that makes sure that existing object files will not get overwritten.
core.notesRef::
When showing commit messages, also show notes which are stored in
the given ref. This ref is expected to contain files named
after the full SHA-1 of the commit they annotate.
after the full SHA-1 of the commit they annotate. The ref
must be fully qualified.
+
If such a file exists in the given ref, the referenced blob is read, and
appended to the commit message, separated by a "Notes:" line. If the
appended to the commit message, separated by a "Notes (<refname>):"
line (shortened to "Notes:" in the case of "refs/notes/commits"). If the
given ref itself does not exist, it is not an error, but means that no
notes should be printed.
+
Expand Down Expand Up @@ -1286,6 +1288,23 @@ mergetool.keepTemporaries::
mergetool.prompt::
Prompt before each invocation of the merge resolution program.

notes.displayRef::
The (fully qualified) refname from which to show notes when
showing commit messages. The value of this variable can be set
to a glob, in which case notes from all matching refs will be
shown. You may also specify this configuration variable
several times. A warning will be issued for refs that do not
exist, but a glob that does not match any refs is silently
ignored.
+
This setting can be overridden with the `GIT_NOTES_DISPLAY_REF`
environment variable, which must be a colon separated list of refs or
globs.
+
The effective value of "core.notesRef" (possibly overridden by
GIT_NOTES_REF) is also implicitly added to the list of refs to be
displayed.

pack.window::
The size of the window used by linkgit:git-pack-objects[1] when no
window size is given on the command line. Defaults to 10.
Expand Down
11 changes: 6 additions & 5 deletions Documentation/git-notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ A typical use of notes is to extend a commit message without having
to change the commit itself. Such commit notes can be shown by `git log`
along with the original commit message. To discern these notes from the
message stored in the commit object, the notes are indented like the
message, after an unindented line saying "Notes:".
message, after an unindented line saying "Notes (<refname>):" (or
"Notes:" for the default setting).

To disable notes, you have to set the config variable core.notesRef to
the empty string. Alternatively, you can set it to a different ref,
something like "refs/notes/bugzilla". This setting can be overridden
by the environment variable "GIT_NOTES_REF".
This command always manipulates the notes specified in "core.notesRef"
(see linkgit:git-config[1]), which can be overridden by GIT_NOTES_REF.
To change which notes are shown by 'git-log', see the
"notes.displayRef" configuration.


SUBCOMMANDS
Expand Down
11 changes: 10 additions & 1 deletion Documentation/pretty-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,18 @@ people using 80-column terminals.
defaults to UTF-8.

--no-notes::
--show-notes::
--show-notes[=<ref>]::
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
there is no `--pretty`, `--format` nor `--oneline` option is
given on the command line.
+
With an optional argument, add this ref to the list of notes. The ref
is taken to be in `refs/notes/` if it is not qualified.

--[no-]standard-notes::
Enable or disable populating the notes ref list from the
'core.notesRef' and 'notes.displayRef' variables (or
corresponding environment overrides). Enabled by default.
See linkgit:git-config[1].
5 changes: 5 additions & 0 deletions builtin-log.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,

if (!rev->show_notes_given && !rev->pretty_given)
rev->show_notes = 1;
if (rev->show_notes)
init_display_notes(&rev->notes_opt);

if (rev->diffopt.pickaxe || rev->diffopt.filter)
rev->always_show_header = 0;
Expand Down Expand Up @@ -1059,6 +1061,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
DIFF_OPT_SET(&rev.diffopt, BINARY);

if (rev.show_notes)
init_display_notes(&rev.notes_opt);

if (!use_stdout)
output_directory = set_outdir(prefix, output_directory);

Expand Down
1 change: 1 addition & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ static inline enum object_type object_type(unsigned int mode)
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"

extern int is_bare_repository_cfg;
extern int is_bare_repository(void);
Expand Down
169 changes: 162 additions & 7 deletions notes.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "utf8.h"
#include "strbuf.h"
#include "tree-walk.h"
#include "string-list.h"
#include "refs.h"

/*
* Use a non-balancing simple 16-tree structure with struct int_node as
Expand Down Expand Up @@ -68,6 +70,9 @@ struct non_note {

struct notes_tree default_notes_tree;

static struct string_list display_notes_refs;
static struct notes_tree **display_notes_trees;

static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
struct int_node *node, unsigned int n);

Expand Down Expand Up @@ -828,6 +833,83 @@ int combine_notes_ignore(unsigned char *cur_sha1,
return 0;
}

static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
int flag, void *cb)
{
struct string_list *refs = cb;
if (!unsorted_string_list_has_string(refs, path))
string_list_append(path, refs);
return 0;
}

void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
{
if (has_glob_specials(glob)) {
for_each_glob_ref(string_list_add_one_ref, glob, list);
} else {
unsigned char sha1[20];
if (get_sha1(glob, sha1))
warning("notes ref %s is invalid", glob);
if (!unsorted_string_list_has_string(list, glob))
string_list_append(glob, list);
}
}

void string_list_add_refs_from_colon_sep(struct string_list *list,
const char *globs)
{
struct strbuf globbuf = STRBUF_INIT;
struct strbuf **split;
int i;

strbuf_addstr(&globbuf, globs);
split = strbuf_split(&globbuf, ':');

for (i = 0; split[i]; i++) {
if (!split[i]->len)
continue;
if (split[i]->buf[split[i]->len-1] == ':')
strbuf_setlen(split[i], split[i]->len-1);
string_list_add_refs_by_glob(list, split[i]->buf);
}

strbuf_list_free(split);
strbuf_release(&globbuf);
}

static int string_list_add_refs_from_list(struct string_list_item *item,
void *cb)
{
struct string_list *list = cb;
string_list_add_refs_by_glob(list, item->string);
return 0;
}

static int notes_display_config(const char *k, const char *v, void *cb)
{
int *load_refs = cb;

if (*load_refs && !strcmp(k, "notes.displayref")) {
if (!v)
config_error_nonbool(k);
string_list_add_refs_by_glob(&display_notes_refs, v);
}

return 0;
}

static const char *default_notes_ref(void)
{
const char *notes_ref = NULL;
if (!notes_ref)
notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
if (!notes_ref)
notes_ref = notes_ref_name; /* value of core.notesRef config */
if (!notes_ref)
notes_ref = GIT_NOTES_DEFAULT_REF;
return notes_ref;
}

void init_notes(struct notes_tree *t, const char *notes_ref,
combine_notes_fn combine_notes, int flags)
{
Expand All @@ -840,11 +922,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
assert(!t->initialized);

if (!notes_ref)
notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
if (!notes_ref)
notes_ref = notes_ref_name; /* value of core.notesRef config */
if (!notes_ref)
notes_ref = GIT_NOTES_DEFAULT_REF;
notes_ref = default_notes_ref();

if (!combine_notes)
combine_notes = combine_notes_concatenate;
Expand All @@ -868,6 +946,63 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
load_subtree(t, &root_tree, t->root, 0);
}

struct load_notes_cb_data {
int counter;
struct notes_tree **trees;
};

static int load_one_display_note_ref(struct string_list_item *item,
void *cb_data)
{
struct load_notes_cb_data *c = cb_data;
struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
init_notes(t, item->string, combine_notes_ignore, 0);
c->trees[c->counter++] = t;
return 0;
}

struct notes_tree **load_notes_trees(struct string_list *refs)
{
struct notes_tree **trees;
struct load_notes_cb_data cb_data;
trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
cb_data.counter = 0;
cb_data.trees = trees;
for_each_string_list(load_one_display_note_ref, refs, &cb_data);
trees[cb_data.counter] = NULL;
return trees;
}

void init_display_notes(struct display_notes_opt *opt)
{
char *display_ref_env;
int load_config_refs = 0;
display_notes_refs.strdup_strings = 1;

assert(!display_notes_trees);

if (!opt || !opt->suppress_default_notes) {
string_list_append(default_notes_ref(), &display_notes_refs);
display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);
if (display_ref_env) {
string_list_add_refs_from_colon_sep(&display_notes_refs,
display_ref_env);
load_config_refs = 0;
} else
load_config_refs = 1;
}

git_config(notes_display_config, &load_config_refs);

if (opt && opt->extra_notes_refs)
for_each_string_list(string_list_add_refs_from_list,
opt->extra_notes_refs,
&display_notes_refs);

display_notes_trees = load_notes_trees(&display_notes_refs);
string_list_clear(&display_notes_refs, 0);
}

void add_note(struct notes_tree *t, const unsigned char *object_sha1,
const unsigned char *note_sha1, combine_notes_fn combine_notes)
{
Expand Down Expand Up @@ -1016,8 +1151,18 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,
if (msglen && msg[msglen - 1] == '\n')
msglen--;

if (flags & NOTES_SHOW_HEADER)
strbuf_addstr(sb, "\nNotes:\n");
if (flags & NOTES_SHOW_HEADER) {
const char *ref = t->ref;
if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
strbuf_addstr(sb, "\nNotes:\n");
} else {
if (!prefixcmp(ref, "refs/"))
ref += 5;
if (!prefixcmp(ref, "notes/"))
ref += 6;
strbuf_addf(sb, "\nNotes (%s):\n", ref);
}
}

for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
linelen = strchrnul(msg_p, '\n') - msg_p;
Expand All @@ -1030,3 +1175,13 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,

free(msg);
}

void format_display_notes(const unsigned char *object_sha1,
struct strbuf *sb, const char *output_encoding, int flags)
{
int i;
assert(display_notes_trees);
for (i = 0; display_notes_trees[i]; i++)
format_note(display_notes_trees[i], object_sha1, sb,
output_encoding, flags);
}
55 changes: 55 additions & 0 deletions notes.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,59 @@ void free_notes(struct notes_tree *t);
void format_note(struct notes_tree *t, const unsigned char *object_sha1,
struct strbuf *sb, const char *output_encoding, int flags);


struct string_list;

struct display_notes_opt {
int suppress_default_notes:1;
struct string_list *extra_notes_refs;
};

/*
* Load the notes machinery for displaying several notes trees.
*
* If 'opt' is not NULL, then it specifies additional settings for the
* displaying:
*
* - suppress_default_notes indicates that the notes from
* core.notesRef and notes.displayRef should not be loaded.
*
* - extra_notes_refs may contain a list of globs (in the same style
* as notes.displayRef) where notes should be loaded from.
*/
void init_display_notes(struct display_notes_opt *opt);

/*
* Append notes for the given 'object_sha1' from all trees set up by
* init_display_notes() to 'sb'. The 'flags' are a bitwise
* combination of
*
* - NOTES_SHOW_HEADER: add a 'Notes (refname):' header
*
* - NOTES_INDENT: indent the notes by 4 places
*
* You *must* call init_display_notes() before using this function.
*/
void format_display_notes(const unsigned char *object_sha1,
struct strbuf *sb, const char *output_encoding, int flags);

/*
* 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);

/*
* Add all refs that match 'glob' to the 'list'.
*/
void string_list_add_refs_by_glob(struct string_list *list, const char *glob);

/*
* Add all refs from a colon-separated glob list 'globs' to the end of
* 'list'. Empty components are ignored. This helper is used to
* parse GIT_NOTES_DISPLAY_REF style environment variables.
*/
void string_list_add_refs_from_colon_sep(struct string_list *list,
const char *globs);

#endif
6 changes: 3 additions & 3 deletions pretty.c
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
}
return 0; /* unknown %g placeholder */
case 'N':
format_note(NULL, commit->object.sha1, sb,
format_display_notes(commit->object.sha1, sb,
git_log_output_encoding ? git_log_output_encoding
: git_commit_encoding, 0);
return 1;
Expand Down Expand Up @@ -1096,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
strbuf_addch(sb, '\n');

if (context->show_notes)
format_note(NULL, commit->object.sha1, sb, encoding,
NOTES_SHOW_HEADER | NOTES_INDENT);
format_display_notes(commit->object.sha1, sb, encoding,
NOTES_SHOW_HEADER | NOTES_INDENT);

free(reencoded);
}
Loading

0 comments on commit 894a9d3

Please sign in to comment.