Skip to content

Commit 894a9d3

Browse files
trastgitster
authored andcommitted
Support showing notes from more than one notes tree
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 <[email protected]> Acked-by: Johan Herland <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6ceeaee commit 894a9d3

File tree

14 files changed

+437
-30
lines changed

14 files changed

+437
-30
lines changed

Documentation/config.txt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,10 +500,12 @@ check that makes sure that existing object files will not get overwritten.
500500
core.notesRef::
501501
When showing commit messages, also show notes which are stored in
502502
the given ref. This ref is expected to contain files named
503-
after the full SHA-1 of the commit they annotate.
503+
after the full SHA-1 of the commit they annotate. The ref
504+
must be fully qualified.
504505
+
505506
If such a file exists in the given ref, the referenced blob is read, and
506-
appended to the commit message, separated by a "Notes:" line. If the
507+
appended to the commit message, separated by a "Notes (<refname>):"
508+
line (shortened to "Notes:" in the case of "refs/notes/commits"). If the
507509
given ref itself does not exist, it is not an error, but means that no
508510
notes should be printed.
509511
+
@@ -1286,6 +1288,23 @@ mergetool.keepTemporaries::
12861288
mergetool.prompt::
12871289
Prompt before each invocation of the merge resolution program.
12881290

1291+
notes.displayRef::
1292+
The (fully qualified) refname from which to show notes when
1293+
showing commit messages. The value of this variable can be set
1294+
to a glob, in which case notes from all matching refs will be
1295+
shown. You may also specify this configuration variable
1296+
several times. A warning will be issued for refs that do not
1297+
exist, but a glob that does not match any refs is silently
1298+
ignored.
1299+
+
1300+
This setting can be overridden with the `GIT_NOTES_DISPLAY_REF`
1301+
environment variable, which must be a colon separated list of refs or
1302+
globs.
1303+
+
1304+
The effective value of "core.notesRef" (possibly overridden by
1305+
GIT_NOTES_REF) is also implicitly added to the list of refs to be
1306+
displayed.
1307+
12891308
pack.window::
12901309
The size of the window used by linkgit:git-pack-objects[1] when no
12911310
window size is given on the command line. Defaults to 10.

Documentation/git-notes.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ A typical use of notes is to extend a commit message without having
2727
to change the commit itself. Such commit notes can be shown by `git log`
2828
along with the original commit message. To discern these notes from the
2929
message stored in the commit object, the notes are indented like the
30-
message, after an unindented line saying "Notes:".
30+
message, after an unindented line saying "Notes (<refname>):" (or
31+
"Notes:" for the default setting).
3132

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

3738

3839
SUBCOMMANDS

Documentation/pretty-options.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,18 @@ people using 80-column terminals.
3030
defaults to UTF-8.
3131

3232
--no-notes::
33-
--show-notes::
33+
--show-notes[=<ref>]::
3434
Show the notes (see linkgit:git-notes[1]) that annotate the
3535
commit, when showing the commit log message. This is the default
3636
for `git log`, `git show` and `git whatchanged` commands when
3737
there is no `--pretty`, `--format` nor `--oneline` option is
3838
given on the command line.
39+
+
40+
With an optional argument, add this ref to the list of notes. The ref
41+
is taken to be in `refs/notes/` if it is not qualified.
42+
43+
--[no-]standard-notes::
44+
Enable or disable populating the notes ref list from the
45+
'core.notesRef' and 'notes.displayRef' variables (or
46+
corresponding environment overrides). Enabled by default.
47+
See linkgit:git-config[1].

builtin-log.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
6060

6161
if (!rev->show_notes_given && !rev->pretty_given)
6262
rev->show_notes = 1;
63+
if (rev->show_notes)
64+
init_display_notes(&rev->notes_opt);
6365

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

1064+
if (rev.show_notes)
1065+
init_display_notes(&rev.notes_opt);
1066+
10621067
if (!use_stdout)
10631068
output_directory = set_outdir(prefix, output_directory);
10641069

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ static inline enum object_type object_type(unsigned int mode)
385385
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
386386
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
387387
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
388+
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
388389

389390
extern int is_bare_repository_cfg;
390391
extern int is_bare_repository(void);

notes.c

Lines changed: 162 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "utf8.h"
66
#include "strbuf.h"
77
#include "tree-walk.h"
8+
#include "string-list.h"
9+
#include "refs.h"
810

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

6971
struct notes_tree default_notes_tree;
7072

73+
static struct string_list display_notes_refs;
74+
static struct notes_tree **display_notes_trees;
75+
7176
static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
7277
struct int_node *node, unsigned int n);
7378

@@ -828,6 +833,83 @@ int combine_notes_ignore(unsigned char *cur_sha1,
828833
return 0;
829834
}
830835

836+
static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
837+
int flag, void *cb)
838+
{
839+
struct string_list *refs = cb;
840+
if (!unsorted_string_list_has_string(refs, path))
841+
string_list_append(path, refs);
842+
return 0;
843+
}
844+
845+
void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
846+
{
847+
if (has_glob_specials(glob)) {
848+
for_each_glob_ref(string_list_add_one_ref, glob, list);
849+
} else {
850+
unsigned char sha1[20];
851+
if (get_sha1(glob, sha1))
852+
warning("notes ref %s is invalid", glob);
853+
if (!unsorted_string_list_has_string(list, glob))
854+
string_list_append(glob, list);
855+
}
856+
}
857+
858+
void string_list_add_refs_from_colon_sep(struct string_list *list,
859+
const char *globs)
860+
{
861+
struct strbuf globbuf = STRBUF_INIT;
862+
struct strbuf **split;
863+
int i;
864+
865+
strbuf_addstr(&globbuf, globs);
866+
split = strbuf_split(&globbuf, ':');
867+
868+
for (i = 0; split[i]; i++) {
869+
if (!split[i]->len)
870+
continue;
871+
if (split[i]->buf[split[i]->len-1] == ':')
872+
strbuf_setlen(split[i], split[i]->len-1);
873+
string_list_add_refs_by_glob(list, split[i]->buf);
874+
}
875+
876+
strbuf_list_free(split);
877+
strbuf_release(&globbuf);
878+
}
879+
880+
static int string_list_add_refs_from_list(struct string_list_item *item,
881+
void *cb)
882+
{
883+
struct string_list *list = cb;
884+
string_list_add_refs_by_glob(list, item->string);
885+
return 0;
886+
}
887+
888+
static int notes_display_config(const char *k, const char *v, void *cb)
889+
{
890+
int *load_refs = cb;
891+
892+
if (*load_refs && !strcmp(k, "notes.displayref")) {
893+
if (!v)
894+
config_error_nonbool(k);
895+
string_list_add_refs_by_glob(&display_notes_refs, v);
896+
}
897+
898+
return 0;
899+
}
900+
901+
static const char *default_notes_ref(void)
902+
{
903+
const char *notes_ref = NULL;
904+
if (!notes_ref)
905+
notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
906+
if (!notes_ref)
907+
notes_ref = notes_ref_name; /* value of core.notesRef config */
908+
if (!notes_ref)
909+
notes_ref = GIT_NOTES_DEFAULT_REF;
910+
return notes_ref;
911+
}
912+
831913
void init_notes(struct notes_tree *t, const char *notes_ref,
832914
combine_notes_fn combine_notes, int flags)
833915
{
@@ -840,11 +922,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
840922
assert(!t->initialized);
841923

842924
if (!notes_ref)
843-
notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);
844-
if (!notes_ref)
845-
notes_ref = notes_ref_name; /* value of core.notesRef config */
846-
if (!notes_ref)
847-
notes_ref = GIT_NOTES_DEFAULT_REF;
925+
notes_ref = default_notes_ref();
848926

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

949+
struct load_notes_cb_data {
950+
int counter;
951+
struct notes_tree **trees;
952+
};
953+
954+
static int load_one_display_note_ref(struct string_list_item *item,
955+
void *cb_data)
956+
{
957+
struct load_notes_cb_data *c = cb_data;
958+
struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
959+
init_notes(t, item->string, combine_notes_ignore, 0);
960+
c->trees[c->counter++] = t;
961+
return 0;
962+
}
963+
964+
struct notes_tree **load_notes_trees(struct string_list *refs)
965+
{
966+
struct notes_tree **trees;
967+
struct load_notes_cb_data cb_data;
968+
trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
969+
cb_data.counter = 0;
970+
cb_data.trees = trees;
971+
for_each_string_list(load_one_display_note_ref, refs, &cb_data);
972+
trees[cb_data.counter] = NULL;
973+
return trees;
974+
}
975+
976+
void init_display_notes(struct display_notes_opt *opt)
977+
{
978+
char *display_ref_env;
979+
int load_config_refs = 0;
980+
display_notes_refs.strdup_strings = 1;
981+
982+
assert(!display_notes_trees);
983+
984+
if (!opt || !opt->suppress_default_notes) {
985+
string_list_append(default_notes_ref(), &display_notes_refs);
986+
display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);
987+
if (display_ref_env) {
988+
string_list_add_refs_from_colon_sep(&display_notes_refs,
989+
display_ref_env);
990+
load_config_refs = 0;
991+
} else
992+
load_config_refs = 1;
993+
}
994+
995+
git_config(notes_display_config, &load_config_refs);
996+
997+
if (opt && opt->extra_notes_refs)
998+
for_each_string_list(string_list_add_refs_from_list,
999+
opt->extra_notes_refs,
1000+
&display_notes_refs);
1001+
1002+
display_notes_trees = load_notes_trees(&display_notes_refs);
1003+
string_list_clear(&display_notes_refs, 0);
1004+
}
1005+
8711006
void add_note(struct notes_tree *t, const unsigned char *object_sha1,
8721007
const unsigned char *note_sha1, combine_notes_fn combine_notes)
8731008
{
@@ -1016,8 +1151,18 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,
10161151
if (msglen && msg[msglen - 1] == '\n')
10171152
msglen--;
10181153

1019-
if (flags & NOTES_SHOW_HEADER)
1020-
strbuf_addstr(sb, "\nNotes:\n");
1154+
if (flags & NOTES_SHOW_HEADER) {
1155+
const char *ref = t->ref;
1156+
if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
1157+
strbuf_addstr(sb, "\nNotes:\n");
1158+
} else {
1159+
if (!prefixcmp(ref, "refs/"))
1160+
ref += 5;
1161+
if (!prefixcmp(ref, "notes/"))
1162+
ref += 6;
1163+
strbuf_addf(sb, "\nNotes (%s):\n", ref);
1164+
}
1165+
}
10211166

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

10311176
free(msg);
10321177
}
1178+
1179+
void format_display_notes(const unsigned char *object_sha1,
1180+
struct strbuf *sb, const char *output_encoding, int flags)
1181+
{
1182+
int i;
1183+
assert(display_notes_trees);
1184+
for (i = 0; display_notes_trees[i]; i++)
1185+
format_note(display_notes_trees[i], object_sha1, sb,
1186+
output_encoding, flags);
1187+
}

notes.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,59 @@ void free_notes(struct notes_tree *t);
198198
void format_note(struct notes_tree *t, const unsigned char *object_sha1,
199199
struct strbuf *sb, const char *output_encoding, int flags);
200200

201+
202+
struct string_list;
203+
204+
struct display_notes_opt {
205+
int suppress_default_notes:1;
206+
struct string_list *extra_notes_refs;
207+
};
208+
209+
/*
210+
* Load the notes machinery for displaying several notes trees.
211+
*
212+
* If 'opt' is not NULL, then it specifies additional settings for the
213+
* displaying:
214+
*
215+
* - suppress_default_notes indicates that the notes from
216+
* core.notesRef and notes.displayRef should not be loaded.
217+
*
218+
* - extra_notes_refs may contain a list of globs (in the same style
219+
* as notes.displayRef) where notes should be loaded from.
220+
*/
221+
void init_display_notes(struct display_notes_opt *opt);
222+
223+
/*
224+
* Append notes for the given 'object_sha1' from all trees set up by
225+
* init_display_notes() to 'sb'. The 'flags' are a bitwise
226+
* combination of
227+
*
228+
* - NOTES_SHOW_HEADER: add a 'Notes (refname):' header
229+
*
230+
* - NOTES_INDENT: indent the notes by 4 places
231+
*
232+
* You *must* call init_display_notes() before using this function.
233+
*/
234+
void format_display_notes(const unsigned char *object_sha1,
235+
struct strbuf *sb, const char *output_encoding, int flags);
236+
237+
/*
238+
* Load the notes tree from each ref listed in 'refs'. The output is
239+
* an array of notes_tree*, terminated by a NULL.
240+
*/
241+
struct notes_tree **load_notes_trees(struct string_list *refs);
242+
243+
/*
244+
* Add all refs that match 'glob' to the 'list'.
245+
*/
246+
void string_list_add_refs_by_glob(struct string_list *list, const char *glob);
247+
248+
/*
249+
* Add all refs from a colon-separated glob list 'globs' to the end of
250+
* 'list'. Empty components are ignored. This helper is used to
251+
* parse GIT_NOTES_DISPLAY_REF style environment variables.
252+
*/
253+
void string_list_add_refs_from_colon_sep(struct string_list *list,
254+
const char *globs);
255+
201256
#endif

pretty.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
775775
}
776776
return 0; /* unknown %g placeholder */
777777
case 'N':
778-
format_note(NULL, commit->object.sha1, sb,
778+
format_display_notes(commit->object.sha1, sb,
779779
git_log_output_encoding ? git_log_output_encoding
780780
: git_commit_encoding, 0);
781781
return 1;
@@ -1096,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
10961096
strbuf_addch(sb, '\n');
10971097

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

11021102
free(reencoded);
11031103
}

0 commit comments

Comments
 (0)