Skip to content

Commit 6956f85

Browse files
trastgitster
authored andcommitted
notes: implement helpers needed for note copying during rewrite
Implement helper functions to load the rewriting config, and to actually copy the notes. Also document the config. Secondly, also implement an undocumented --for-rewrite=<cmd> option to 'git notes copy' which is used like --stdin, but also puts the configuration for <cmd> into effect. It will be needed to support the copying in git-rebase. Signed-off-by: Thomas Rast <[email protected]> Acked-by: Johan Herland <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 160baa0 commit 6956f85

File tree

8 files changed

+385
-8
lines changed

8 files changed

+385
-8
lines changed

Documentation/config.txt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,36 @@ The effective value of "core.notesRef" (possibly overridden by
13051305
GIT_NOTES_REF) is also implicitly added to the list of refs to be
13061306
displayed.
13071307

1308+
notes.rewrite.<command>::
1309+
When rewriting commits with <command> (currently `amend` or
1310+
`rebase`) and this variable is set to `true`, git
1311+
automatically copies your notes from the original to the
1312+
rewritten commit. Defaults to `true`, but see
1313+
"notes.rewriteRef" below.
1314+
+
1315+
This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
1316+
environment variable, which must be a colon separated list of refs or
1317+
globs.
1318+
1319+
notes.rewriteMode::
1320+
When copying notes during a rewrite (see the
1321+
"notes.rewrite.<command>" option), determines what to do if
1322+
the target commit already has a note. Must be one of
1323+
`overwrite`, `concatenate`, or `ignore`. Defaults to
1324+
`concatenate`.
1325+
+
1326+
This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
1327+
environment variable.
1328+
1329+
notes.rewriteRef::
1330+
When copying notes during a rewrite, specifies the (fully
1331+
qualified) ref whose notes should be copied. The ref may be a
1332+
glob, in which case notes in all matching refs will be copied.
1333+
You may also specify this configuration several times.
1334+
+
1335+
Does not have a default value; you must configure this variable to
1336+
enable note rewriting.
1337+
13081338
pack.window::
13091339
The size of the window used by linkgit:git-pack-objects[1] when no
13101340
window size is given on the command line. Defaults to 10.

Documentation/git-notes.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ This command always manipulates the notes specified in "core.notesRef"
3535
To change which notes are shown by 'git-log', see the
3636
"notes.displayRef" configuration.
3737

38+
See the description of "notes.rewrite.<command>" in
39+
linkgit:git-config[1] for a way of carrying your notes across commands
40+
that rewrite commits.
41+
3842

3943
SUBCOMMANDS
4044
-----------

Documentation/githooks.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,10 @@ The 'extra-info' is again command-dependent. If it is empty, the
335335
preceding SP is also omitted. Currently, no commands pass any
336336
'extra-info'.
337337

338+
The hook always runs after the automatic note copying (see
339+
"notes.rewrite.<command>" in linkgit:git-config.txt) has happened, and
340+
thus has access to these notes.
341+
338342
The following command-specific comments apply:
339343

340344
rebase::

builtin-notes.c

Lines changed: 131 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "exec_cmd.h"
1717
#include "run-command.h"
1818
#include "parse-options.h"
19+
#include "string-list.h"
1920

2021
static const char * const git_notes_usage[] = {
2122
"git notes [list [<object>]]",
@@ -278,14 +279,121 @@ int commit_notes(struct notes_tree *t, const char *msg)
278279
return 0;
279280
}
280281

281-
int notes_copy_from_stdin(int force)
282+
283+
combine_notes_fn *parse_combine_notes_fn(const char *v)
284+
{
285+
if (!strcasecmp(v, "overwrite"))
286+
return combine_notes_overwrite;
287+
else if (!strcasecmp(v, "ignore"))
288+
return combine_notes_ignore;
289+
else if (!strcasecmp(v, "concatenate"))
290+
return combine_notes_concatenate;
291+
else
292+
return NULL;
293+
}
294+
295+
static int notes_rewrite_config(const char *k, const char *v, void *cb)
296+
{
297+
struct notes_rewrite_cfg *c = cb;
298+
if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
299+
c->enabled = git_config_bool(k, v);
300+
return 0;
301+
} else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
302+
if (!v)
303+
config_error_nonbool(k);
304+
c->combine = parse_combine_notes_fn(v);
305+
if (!c->combine) {
306+
error("Bad notes.rewriteMode value: '%s'", v);
307+
return 1;
308+
}
309+
return 0;
310+
} else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
311+
/* note that a refs/ prefix is implied in the
312+
* underlying for_each_glob_ref */
313+
if (!prefixcmp(v, "refs/notes/"))
314+
string_list_add_refs_by_glob(c->refs, v);
315+
else
316+
warning("Refusing to rewrite notes in %s"
317+
" (outside of refs/notes/)", v);
318+
return 0;
319+
}
320+
321+
return 0;
322+
}
323+
324+
325+
struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
326+
{
327+
struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
328+
const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
329+
const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
330+
c->cmd = cmd;
331+
c->enabled = 1;
332+
c->combine = combine_notes_concatenate;
333+
c->refs = xcalloc(1, sizeof(struct string_list));
334+
c->refs->strdup_strings = 1;
335+
c->refs_from_env = 0;
336+
c->mode_from_env = 0;
337+
if (rewrite_mode_env) {
338+
c->mode_from_env = 1;
339+
c->combine = parse_combine_notes_fn(rewrite_mode_env);
340+
if (!c->combine)
341+
error("Bad " GIT_NOTES_REWRITE_MODE_ENVIRONMENT
342+
" value: '%s'", rewrite_mode_env);
343+
}
344+
if (rewrite_refs_env) {
345+
c->refs_from_env = 1;
346+
string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
347+
}
348+
git_config(notes_rewrite_config, c);
349+
if (!c->enabled || !c->refs->nr) {
350+
string_list_clear(c->refs, 0);
351+
free(c->refs);
352+
free(c);
353+
return NULL;
354+
}
355+
c->trees = load_notes_trees(c->refs);
356+
string_list_clear(c->refs, 0);
357+
free(c->refs);
358+
return c;
359+
}
360+
361+
int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
362+
const unsigned char *from_obj, const unsigned char *to_obj)
363+
{
364+
int ret = 0;
365+
int i;
366+
for (i = 0; c->trees[i]; i++)
367+
ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
368+
return ret;
369+
}
370+
371+
void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
372+
{
373+
int i;
374+
for (i = 0; c->trees[i]; i++) {
375+
commit_notes(c->trees[i], "Notes added by 'git notes copy'");
376+
free_notes(c->trees[i]);
377+
}
378+
free(c->trees);
379+
free(c);
380+
}
381+
382+
int notes_copy_from_stdin(int force, const char *rewrite_cmd)
282383
{
283384
struct strbuf buf = STRBUF_INIT;
385+
struct notes_rewrite_cfg *c = NULL;
284386
struct notes_tree *t;
285387
int ret = 0;
286388

287-
init_notes(NULL, NULL, NULL, 0);
288-
t = &default_notes_tree;
389+
if (rewrite_cmd) {
390+
c = init_copy_notes_for_rewrite(rewrite_cmd);
391+
if (!c)
392+
return 0;
393+
} else {
394+
init_notes(NULL, NULL, NULL, 0);
395+
t = &default_notes_tree;
396+
}
289397

290398
while (strbuf_getline(&buf, stdin, '\n') != EOF) {
291399
unsigned char from_obj[20], to_obj[20];
@@ -302,7 +410,11 @@ int notes_copy_from_stdin(int force)
302410
if (get_sha1(split[1]->buf, to_obj))
303411
die("Failed to resolve '%s' as a valid ref.", split[1]->buf);
304412

305-
err = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);
413+
if (rewrite_cmd)
414+
err = copy_note_for_rewrite(c, from_obj, to_obj);
415+
else
416+
err = copy_note(t, from_obj, to_obj, force,
417+
combine_notes_overwrite);
306418

307419
if (err) {
308420
error("Failed to copy notes from '%s' to '%s'",
@@ -313,8 +425,12 @@ int notes_copy_from_stdin(int force)
313425
strbuf_list_free(split);
314426
}
315427

316-
commit_notes(t, "Notes added by 'git notes copy'");
317-
free_notes(t);
428+
if (!rewrite_cmd) {
429+
commit_notes(t, "Notes added by 'git notes copy'");
430+
free_notes(t);
431+
} else {
432+
finish_copy_notes_for_rewrite(c);
433+
}
318434
return ret;
319435
}
320436

@@ -330,6 +446,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
330446
remove = 0, prune = 0, force = 0, from_stdin = 0;
331447
int given_object = 0, i = 1, retval = 0;
332448
struct msg_arg msg = { 0, 0, STRBUF_INIT };
449+
const char *rewrite_cmd = NULL;
333450
struct option options[] = {
334451
OPT_GROUP("Notes options"),
335452
OPT_CALLBACK('m', "message", &msg, "MSG",
@@ -342,6 +459,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
342459
"reuse specified note object", parse_reuse_arg),
343460
OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
344461
OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
462+
OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
463+
"load rewriting config for <command> (implies --stdin)"),
345464
OPT_END()
346465
};
347466

@@ -390,19 +509,23 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
390509
usage_with_options(git_notes_usage, options);
391510
}
392511

512+
if (!copy && rewrite_cmd) {
513+
error("cannot use --for-rewrite with %s subcommand.", argv[0]);
514+
usage_with_options(git_notes_usage, options);
515+
}
393516
if (!copy && from_stdin) {
394517
error("cannot use --stdin with %s subcommand.", argv[0]);
395518
usage_with_options(git_notes_usage, options);
396519
}
397520

398521
if (copy) {
399522
const char *from_ref;
400-
if (from_stdin) {
523+
if (from_stdin || rewrite_cmd) {
401524
if (argc > 1) {
402525
error("too many parameters");
403526
usage_with_options(git_notes_usage, options);
404527
} else {
405-
return notes_copy_from_stdin(force);
528+
return notes_copy_from_stdin(force, rewrite_cmd);
406529
}
407530
}
408531
if (argc < 3) {

builtin.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@ extern int commit_tree(const char *msg, unsigned char *tree,
2020
struct commit_list *parents, unsigned char *ret,
2121
const char *author);
2222
extern int commit_notes(struct notes_tree *t, const char *msg);
23+
24+
struct notes_rewrite_cfg {
25+
struct notes_tree **trees;
26+
const char *cmd;
27+
int enabled;
28+
combine_notes_fn *combine;
29+
struct string_list *refs;
30+
int refs_from_env;
31+
int mode_from_env;
32+
};
33+
34+
combine_notes_fn *parse_combine_notes_fn(const char *v);
35+
struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
36+
int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
37+
const unsigned char *from_obj, const unsigned char *to_obj);
38+
void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
39+
2340
extern int check_pager_config(const char *cmd);
2441

2542
extern int cmd_add(int argc, const char **argv, const char *prefix);

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ static inline enum object_type object_type(unsigned int mode)
386386
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
387387
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
388388
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
389+
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
390+
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
389391

390392
extern int is_bare_repository_cfg;
391393
extern int is_bare_repository(void);

0 commit comments

Comments
 (0)