builtin-notes: Add -c/-C options for reusing notes
Inspired by the -c/-C options to "git commit", we teach these options to "git notes add/append" to allow reuse of note objects. With this patch in place, it is now easy to copy or move notes between objects. For example, to copy object A's notes to object B: git notes add [-f] -C $(git notes list A) B To move instead of copying, you simply remove the notes from the source object afterwards, e.g.: git notes remove A The patch includes tests verifying correct behaviour of the new functionality. Signed-off-by: Johan Herland <johan@herland.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Johan Herland committed
Feb 13, 2010 at 22:28 UTC
0691cff7dca6b68569183be991d59ebb72b6170e
3 files changed
+179
-12
Documentation/git-notes.txt
+10
-2
@@ -9,8 +9,8 @@ SYNOPSIS
9
--------
10
[verse]
11
'git notes' [list [<object>]]
12
-'git notes' add [-f] [-F <file> | -m <msg>] [<object>]
13
-'git notes' append [-F <file> | -m <msg>] [<object>]
12
+'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
13
+'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
14
'git notes' edit [<object>]
15
'git notes' show [<object>]
16
'git notes' remove [<object>]
@@ -84,6 +84,14 @@ OPTIONS
84
Take the note message from the given file. Use '-' to
85
read the note message from the standard input.
86
87
+-C <object>::
88
+--reuse-message=<object>::
89
+ Reuse the note message from the given note object.
90
+
91
+-c <object>::
92
+--reedit-message=<object>::
93
+ Like '-C', but with '-c' the editor is invoked, so that
94
+ the user can further edit the note message.
95
96
Author
97
------
builtin-notes.c
+53
-10
@@ -19,8 +19,8 @@
19
20
static const char * const git_notes_usage[] = {
21
"git notes [list [<object>]]",
22
- "git notes add [-f] [-m <msg> | -F <file>] [<object>]",
23
- "git notes append [-m <msg> | -F <file>] [<object>]",
22
+ "git notes add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
23
+ "git notes append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
24
"git notes edit [<object>]",
25
"git notes show [<object>]",
26
"git notes remove [<object>]",
@@ -36,6 +36,7 @@ static const char note_template[] =
36
37
struct msg_arg {
38
int given;
39
+ int use_editor;
40
struct strbuf buf;
41
};
42
@@ -104,7 +105,7 @@ static void create_note(const unsigned char *object, struct msg_arg *msg,
105
{
106
char *path = NULL;
107
107
- if (!msg->given) {
108
+ if (msg->use_editor || !msg->given) {
109
int fd;
110
111
/* write the template message before editing: */
@@ -113,13 +114,16 @@ static void create_note(const unsigned char *object, struct msg_arg *msg,
114
if (fd < 0)
115
die_errno("could not create file '%s'", path);
116
116
- if (prev && !append_only)
117
+ if (msg->given)
118
+ write_or_die(fd, msg->buf.buf, msg->buf.len);
119
+ else if (prev && !append_only)
120
write_note_data(fd, prev);
121
write_or_die(fd, note_template, strlen(note_template));
122
123
write_commented_object(fd, object);
124
125
close(fd);
126
+ strbuf_reset(&(msg->buf));
127
128
if (launch_editor(path, &(msg->buf), NULL)) {
129
die("Please supply the note contents using either -m" \
@@ -199,6 +203,40 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset)
203
return 0;
204
}
205
206
+static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
207
+{
208
+ struct msg_arg *msg = opt->value;
209
+ char *buf;
210
+ unsigned char object[20];
211
+ enum object_type type;
212
+ unsigned long len;
213
+
214
+ if (!arg)
215
+ return -1;
216
+
217
+ if (msg->buf.len)
218
+ strbuf_addstr(&(msg->buf), "\n");
219
+
220
+ if (get_sha1(arg, object))
221
+ die("Failed to resolve '%s' as a valid ref.", arg);
222
+ if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
223
+ free(buf);
224
+ die("Failed to read object '%s'.", arg);;
225
+ }
226
+ strbuf_add(&(msg->buf), buf, len);
227
+ free(buf);
228
+
229
+ msg->given = 1;
230
+ return 0;
231
+}
232
+
233
+static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
234
+{
235
+ struct msg_arg *msg = opt->value;
236
+ msg->use_editor = 1;
237
+ return parse_reuse_arg(opt, arg, unset);
238
+}
239
+
240
int commit_notes(struct notes_tree *t, const char *msg)
241
{
242
struct commit_list *parent;
@@ -250,13 +288,17 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
288
int list = 0, add = 0, append = 0, edit = 0, show = 0, remove = 0,
289
prune = 0, force = 0;
290
int given_object;
253
- struct msg_arg msg = { 0, STRBUF_INIT };
291
+ struct msg_arg msg = { 0, 0, STRBUF_INIT };
292
struct option options[] = {
293
OPT_GROUP("Notes options"),
294
OPT_CALLBACK('m', "message", &msg, "MSG",
295
"note contents as a string", parse_msg_arg),
296
OPT_CALLBACK('F', "file", &msg, "FILE",
297
"note contents in a file", parse_file_arg),
298
+ OPT_CALLBACK('c', "reedit-message", &msg, "OBJECT",
299
+ "reuse and edit specified note object", parse_reedit_arg),
300
+ OPT_CALLBACK('C', "reuse-message", &msg, "OBJECT",
301
+ "reuse specified note object", parse_reuse_arg),
302
OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
303
OPT_END()
304
};
@@ -286,17 +328,17 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
328
usage_with_options(git_notes_usage, options);
329
330
if (msg.given && !(add || append || edit)) {
289
- error("cannot use -m/-F options with %s subcommand.", argv[0]);
331
+ error("cannot use -m/-F/-c/-C options with %s subcommand.",
332
+ argv[0]);
333
usage_with_options(git_notes_usage, options);
334
}
335
336
if (msg.given && edit) {
294
- fprintf(stderr, "The -m and -F options has been deprecated for"
295
- " the 'edit' subcommand.\n"
296
- "Please use 'git notes add -f -m/-F' instead.\n");
337
+ fprintf(stderr, "The -m/-F/-c/-C options have been deprecated "
338
+ "for the 'edit' subcommand.\n"
339
+ "Please use 'git notes add -f -m/-F/-c/-C' instead.\n");
340
}
341
299
-
342
if (force && !add) {
343
error("cannot use -f option with %s subcommand.", argv[0]);
344
usage_with_options(git_notes_usage, options);
@@ -359,6 +401,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
401
402
if (remove) {
403
msg.given = 1;
404
+ msg.use_editor = 0;
405
strbuf_reset(&(msg.buf));
406
}
407
t/t3301-notes.sh
+116
@@ -465,4 +465,120 @@ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
465
test_cmp expect actual
466
'
467
468
+cat > expect << EOF
469
+commit 2ede89468182a62d0bde2583c736089bcf7d7e92
470
+Author: A U Thor <author@example.com>
471
+Date: Thu Apr 7 15:19:13 2005 -0700
472
+
473
+ 7th
474
+
475
+Notes:
476
+ other note
477
+EOF
478
+
479
+test_expect_success 'create note from other note with "git notes add -C"' '
480
+ : > a7 &&
481
+ git add a7 &&
482
+ test_tick &&
483
+ git commit -m 7th &&
484
+ git notes add -C $(git notes list HEAD^) &&
485
+ git log -1 > actual &&
486
+ test_cmp expect actual &&
487
+ test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
488
+'
489
+
490
+test_expect_success 'create note from non-existing note with "git notes add -C" fails' '
491
+ : > a8 &&
492
+ git add a8 &&
493
+ test_tick &&
494
+ git commit -m 8th &&
495
+ test_must_fail git notes add -C deadbeef &&
496
+ test_must_fail git notes list HEAD
497
+'
498
+
499
+cat > expect << EOF
500
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
501
+Author: A U Thor <author@example.com>
502
+Date: Thu Apr 7 15:21:13 2005 -0700
503
+
504
+ 9th
505
+
506
+Notes:
507
+ yet another note
508
+EOF
509
+
510
+test_expect_success 'create note from other note with "git notes add -c"' '
511
+ : > a9 &&
512
+ git add a9 &&
513
+ test_tick &&
514
+ git commit -m 9th &&
515
+ MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
516
+ git log -1 > actual &&
517
+ test_cmp expect actual
518
+'
519
+
520
+test_expect_success 'create note from non-existing note with "git notes add -c" fails' '
521
+ : > a10 &&
522
+ git add a10 &&
523
+ test_tick &&
524
+ git commit -m 10th &&
525
+ test_must_fail MSG="yet another note" git notes add -c deadbeef &&
526
+ test_must_fail git notes list HEAD
527
+'
528
+
529
+cat > expect << EOF
530
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
531
+Author: A U Thor <author@example.com>
532
+Date: Thu Apr 7 15:21:13 2005 -0700
533
+
534
+ 9th
535
+
536
+Notes:
537
+ yet another note
538
+$whitespace
539
+ yet another note
540
+EOF
541
+
542
+test_expect_success 'append to note from other note with "git notes append -C"' '
543
+ git notes append -C $(git notes list HEAD^) HEAD^ &&
544
+ git log -1 HEAD^ > actual &&
545
+ test_cmp expect actual
546
+'
547
+
548
+cat > expect << EOF
549
+commit ffed603236bfa3891c49644257a83598afe8ae5a
550
+Author: A U Thor <author@example.com>
551
+Date: Thu Apr 7 15:22:13 2005 -0700
552
+
553
+ 10th
554
+
555
+Notes:
556
+ other note
557
+EOF
558
+
559
+test_expect_success 'create note from other note with "git notes append -c"' '
560
+ MSG="other note" git notes append -c $(git notes list HEAD^) &&
561
+ git log -1 > actual &&
562
+ test_cmp expect actual
563
+'
564
+
565
+cat > expect << EOF
566
+commit ffed603236bfa3891c49644257a83598afe8ae5a
567
+Author: A U Thor <author@example.com>
568
+Date: Thu Apr 7 15:22:13 2005 -0700
569
+
570
+ 10th
571
+
572
+Notes:
573
+ other note
574
+$whitespace
575
+ yet another note
576
+EOF
577
+
578
+test_expect_success 'append to note from other note with "git notes append -c"' '
579
+ MSG="yet another note" git notes append -c $(git notes list HEAD) &&
580
+ git log -1 > actual &&
581
+ test_cmp expect actual
582
+'
583
+
584
test_done