@matheusbsbs / matheus-bagliano / commits / 6956f858f6
Fork 0

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 <trast@student.ethz.ch> Acked-by: Johan Herland <johan@herland.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Thomas Rast committed Mar 12, 2010 at 18:04 UTC 6956f858f6237d426fa422949033e3c558104802
8 files changed +385 -8
Documentation/config.txt
+30
@@ -1305,6 +1305,36 @@ 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
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 +
1338 pack.window::
1339 The size of the window used by linkgit:git-pack-objects[1] when no
1340 window size is given on the command line. Defaults to 10.
Documentation/git-notes.txt
+4
@@ -35,6 +35,10 @@ This command always manipulates the notes specified in "core.notesRef"
35 To change which notes are shown by 'git-log', see the
36 "notes.displayRef" configuration.
37
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 +
42
43 SUBCOMMANDS
44 -----------
Documentation/githooks.txt
+4
@@ -335,6 +335,10 @@ The 'extra-info' is again command-dependent. If it is empty, the
335 preceding SP is also omitted. Currently, no commands pass any
336 'extra-info'.
337
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 +
342 The following command-specific comments apply:
343
344 rebase::
builtin-notes.c
+131 -8
@@ -16,6 +16,7 @@
16 #include "exec_cmd.h"
17 #include "run-command.h"
18 #include "parse-options.h"
19 +#include "string-list.h"
20
21 static const char * const git_notes_usage[] = {
22 "git notes [list [<object>]]",
@@ -278,14 +279,121 @@ int commit_notes(struct notes_tree *t, const char *msg)
279 return 0;
280 }
281
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)
383 {
384 struct strbuf buf = STRBUF_INIT;
385 + struct notes_rewrite_cfg *c = NULL;
386 struct notes_tree *t;
387 int ret = 0;
388
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 + }
397
398 while (strbuf_getline(&buf, stdin, '\n') != EOF) {
399 unsigned char from_obj[20], to_obj[20];
@@ -302,7 +410,11 @@ int notes_copy_from_stdin(int force)
410 if (get_sha1(split[1]->buf, to_obj))
411 die("Failed to resolve '%s' as a valid ref.", split[1]->buf);
412
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);
418
419 if (err) {
420 error("Failed to copy notes from '%s' to '%s'",
@@ -313,8 +425,12 @@ int notes_copy_from_stdin(int force)
425 strbuf_list_free(split);
426 }
427
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 + }
434 return ret;
435 }
436
@@ -330,6 +446,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
446 remove = 0, prune = 0, force = 0, from_stdin = 0;
447 int given_object = 0, i = 1, retval = 0;
448 struct msg_arg msg = { 0, 0, STRBUF_INIT };
449 + const char *rewrite_cmd = NULL;
450 struct option options[] = {
451 OPT_GROUP("Notes options"),
452 OPT_CALLBACK('m', "message", &msg, "MSG",
@@ -342,6 +459,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
459 "reuse specified note object", parse_reuse_arg),
460 OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
461 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)"),
464 OPT_END()
465 };
466
@@ -390,6 +509,10 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
509 usage_with_options(git_notes_usage, options);
510 }
511
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 + }
516 if (!copy && from_stdin) {
517 error("cannot use --stdin with %s subcommand.", argv[0]);
518 usage_with_options(git_notes_usage, options);
@@ -397,12 +520,12 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
520
521 if (copy) {
522 const char *from_ref;
400 - if (from_stdin) {
523 + if (from_stdin || rewrite_cmd) {
524 if (argc > 1) {
525 error("too many parameters");
526 usage_with_options(git_notes_usage, options);
527 } else {
405 - return notes_copy_from_stdin(force);
528 + return notes_copy_from_stdin(force, rewrite_cmd);
529 }
530 }
531 if (argc < 3) {
builtin.h
+17
@@ -20,6 +20,23 @@ extern int commit_tree(const char *msg, unsigned char *tree,
20 struct commit_list *parents, unsigned char *ret,
21 const char *author);
22 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 +
40 extern int check_pager_config(const char *cmd);
41
42 extern int cmd_add(int argc, const char **argv, const char *prefix);
cache.h
+2
@@ -386,6 +386,8 @@ static inline enum object_type object_type(unsigned int mode)
386 #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
387 #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
388 #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"
391
392 extern int is_bare_repository_cfg;
393 extern int is_bare_repository(void);
t/t3301-notes.sh
+195
@@ -810,4 +810,199 @@ test_expect_success 'git notes copy --stdin' '
810 test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
811 '
812
813 +cat > expect << EOF
814 +commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
815 +Author: A U Thor <author@example.com>
816 +Date: Thu Apr 7 15:27:13 2005 -0700
817 +
818 + 15th
819 +
820 +commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
821 +Author: A U Thor <author@example.com>
822 +Date: Thu Apr 7 15:26:13 2005 -0700
823 +
824 + 14th
825 +EOF
826 +
827 +test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
828 + test_commit 14th &&
829 + test_commit 15th &&
830 + (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
831 + echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
832 + git notes copy --for-rewrite=foo &&
833 + git log -2 > output &&
834 + test_cmp expect output
835 +'
836 +
837 +cat > expect << EOF
838 +commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
839 +Author: A U Thor <author@example.com>
840 +Date: Thu Apr 7 15:27:13 2005 -0700
841 +
842 + 15th
843 +
844 +Notes (other):
845 + yet another note
846 +$whitespace
847 + yet another note
848 +
849 +commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
850 +Author: A U Thor <author@example.com>
851 +Date: Thu Apr 7 15:26:13 2005 -0700
852 +
853 + 14th
854 +
855 +Notes (other):
856 + other note
857 +$whitespace
858 + yet another note
859 +EOF
860 +
861 +test_expect_success 'git notes copy --for-rewrite (enabled)' '
862 + git config notes.rewriteMode overwrite &&
863 + git config notes.rewriteRef "refs/notes/*" &&
864 + (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
865 + echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
866 + git notes copy --for-rewrite=foo &&
867 + git log -2 > output &&
868 + test_cmp expect output
869 +'
870 +
871 +test_expect_success 'git notes copy --for-rewrite (disabled)' '
872 + git config notes.rewrite.bar false &&
873 + echo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |
874 + git notes copy --for-rewrite=bar &&
875 + git log -2 > output &&
876 + test_cmp expect output
877 +'
878 +
879 +cat > expect << EOF
880 +commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
881 +Author: A U Thor <author@example.com>
882 +Date: Thu Apr 7 15:27:13 2005 -0700
883 +
884 + 15th
885 +
886 +Notes (other):
887 + a fresh note
888 +EOF
889 +
890 +test_expect_success 'git notes copy --for-rewrite (overwrite)' '
891 + git notes add -f -m"a fresh note" HEAD^ &&
892 + echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
893 + git notes copy --for-rewrite=foo &&
894 + git log -1 > output &&
895 + test_cmp expect output
896 +'
897 +
898 +test_expect_success 'git notes copy --for-rewrite (ignore)' '
899 + git config notes.rewriteMode ignore &&
900 + echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
901 + git notes copy --for-rewrite=foo &&
902 + git log -1 > output &&
903 + test_cmp expect output
904 +'
905 +
906 +cat > expect << EOF
907 +commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
908 +Author: A U Thor <author@example.com>
909 +Date: Thu Apr 7 15:27:13 2005 -0700
910 +
911 + 15th
912 +
913 +Notes (other):
914 + a fresh note
915 + another fresh note
916 +EOF
917 +
918 +test_expect_success 'git notes copy --for-rewrite (append)' '
919 + git notes add -f -m"another fresh note" HEAD^ &&
920 + git config notes.rewriteMode concatenate &&
921 + echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
922 + git notes copy --for-rewrite=foo &&
923 + git log -1 > output &&
924 + test_cmp expect output
925 +'
926 +
927 +cat > expect << EOF
928 +commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
929 +Author: A U Thor <author@example.com>
930 +Date: Thu Apr 7 15:27:13 2005 -0700
931 +
932 + 15th
933 +
934 +Notes (other):
935 + a fresh note
936 + another fresh note
937 + append 1
938 + append 2
939 +EOF
940 +
941 +test_expect_success 'git notes copy --for-rewrite (append two to one)' '
942 + git notes add -f -m"append 1" HEAD^ &&
943 + git notes add -f -m"append 2" HEAD^^ &&
944 + (echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
945 + echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
946 + git notes copy --for-rewrite=foo &&
947 + git log -1 > output &&
948 + test_cmp expect output
949 +'
950 +
951 +test_expect_success 'git notes copy --for-rewrite (append empty)' '
952 + git notes remove HEAD^ &&
953 + echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
954 + git notes copy --for-rewrite=foo &&
955 + git log -1 > output &&
956 + test_cmp expect output
957 +'
958 +
959 +cat > expect << EOF
960 +commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
961 +Author: A U Thor <author@example.com>
962 +Date: Thu Apr 7 15:27:13 2005 -0700
963 +
964 + 15th
965 +
966 +Notes (other):
967 + replacement note 1
968 +EOF
969 +
970 +test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
971 + git notes add -f -m"replacement note 1" HEAD^ &&
972 + echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
973 + GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&
974 + git log -1 > output &&
975 + test_cmp expect output
976 +'
977 +
978 +cat > expect << EOF
979 +commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
980 +Author: A U Thor <author@example.com>
981 +Date: Thu Apr 7 15:27:13 2005 -0700
982 +
983 + 15th
984 +
985 +Notes (other):
986 + replacement note 2
987 +EOF
988 +
989 +test_expect_success 'GIT_NOTES_REWRITE_REF works' '
990 + git config notes.rewriteMode overwrite &&
991 + git notes add -f -m"replacement note 2" HEAD^ &&
992 + git config --unset-all notes.rewriteRef &&
993 + echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
994 + GIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \
995 + git notes copy --for-rewrite=foo &&
996 + git log -1 > output &&
997 + test_cmp expect output
998 +'
999 +
1000 +test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
1001 + git config notes.rewriteRef refs/notes/other &&
1002 + git notes add -f -m"replacement note 3" HEAD^ &&
1003 + echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
1004 + GIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&
1005 + git log -1 > output &&
1006 + test_cmp expect output
1007 +'
1008 test_done
t/test-lib.sh
+2
@@ -56,6 +56,8 @@ unset SHA1_FILE_DIRECTORIES
56 unset SHA1_FILE_DIRECTORY
57 unset GIT_NOTES_REF
58 unset GIT_NOTES_DISPLAY_REF
59 +unset GIT_NOTES_REWRITE_REF
60 +unset GIT_NOTES_REWRITE_MODE
61 GIT_MERGE_VERBOSITY=5
62 export GIT_MERGE_VERBOSITY
63 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME