notes: implement 'git notes copy --stdin'
This implements a mass-copy command that takes a sequence of lines in the format <from-sha1> SP <to-sha1> [ SP <rest> ] LF on stdin, and copies each <from-sha1>'s notes to the <to-sha1>. The <rest> is ignored. The intent, of course, is that this can read the same input that the 'post-rewrite' hook gets. The copy_note() function is exposed for everyone's and in particular the next commit's use. 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
160baa0d9cbdfcdb6251aa5ede77c59c0d53edfd
5 files changed
+127
-2
Documentation/git-notes.txt
+11
-1
@@ -10,7 +10,7 @@ SYNOPSIS
10
[verse]
11
'git notes' [list [<object>]]
12
'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
13
-'git notes' copy [-f] <from-object> <to-object>
13
+'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
14
'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
15
'git notes' edit [<object>]
16
'git notes' show [<object>]
@@ -56,6 +56,16 @@ copy::
56
objects has none. (use -f to overwrite existing notes to the
57
second object). This subcommand is equivalent to:
58
`git notes add [-f] -C $(git notes list <from-object>) <to-object>`
59
++
60
+In `\--stdin` mode, take lines in the format
61
++
62
+----------
63
+<from-object> SP <to-object> [ SP <rest> ] LF
64
+----------
65
++
66
+on standard input, and copy the notes from each <from-object> to its
67
+corresponding <to-object>. (The optional `<rest>` is ignored so that
68
+the command can read the input given to the `post-rewrite` hook.)
69
70
append::
71
Append to the notes of an existing object (defaults to HEAD).
builtin-notes.c
+55
-1
@@ -278,6 +278,46 @@ int commit_notes(struct notes_tree *t, const char *msg)
278
return 0;
279
}
280
281
+int notes_copy_from_stdin(int force)
282
+{
283
+ struct strbuf buf = STRBUF_INIT;
284
+ struct notes_tree *t;
285
+ int ret = 0;
286
+
287
+ init_notes(NULL, NULL, NULL, 0);
288
+ t = &default_notes_tree;
289
+
290
+ while (strbuf_getline(&buf, stdin, '\n') != EOF) {
291
+ unsigned char from_obj[20], to_obj[20];
292
+ struct strbuf **split;
293
+ int err;
294
+
295
+ split = strbuf_split(&buf, ' ');
296
+ if (!split[0] || !split[1])
297
+ die("Malformed input line: '%s'.", buf.buf);
298
+ strbuf_rtrim(split[0]);
299
+ strbuf_rtrim(split[1]);
300
+ if (get_sha1(split[0]->buf, from_obj))
301
+ die("Failed to resolve '%s' as a valid ref.", split[0]->buf);
302
+ if (get_sha1(split[1]->buf, to_obj))
303
+ die("Failed to resolve '%s' as a valid ref.", split[1]->buf);
304
+
305
+ err = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);
306
+
307
+ if (err) {
308
+ error("Failed to copy notes from '%s' to '%s'",
309
+ split[0]->buf, split[1]->buf);
310
+ ret = 1;
311
+ }
312
+
313
+ strbuf_list_free(split);
314
+ }
315
+
316
+ commit_notes(t, "Notes added by 'git notes copy'");
317
+ free_notes(t);
318
+ return ret;
319
+}
320
+
321
int cmd_notes(int argc, const char **argv, const char *prefix)
322
{
323
struct notes_tree *t;
@@ -287,7 +327,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
327
char logmsg[100];
328
329
int list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,
290
- remove = 0, prune = 0, force = 0;
330
+ remove = 0, prune = 0, force = 0, from_stdin = 0;
331
int given_object = 0, i = 1, retval = 0;
332
struct msg_arg msg = { 0, 0, STRBUF_INIT };
333
struct option options[] = {
@@ -301,6 +341,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
341
OPT_CALLBACK('C', "reuse-message", &msg, "OBJECT",
342
"reuse specified note object", parse_reuse_arg),
343
OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
344
+ OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
345
OPT_END()
346
};
347
@@ -349,8 +390,21 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
390
usage_with_options(git_notes_usage, options);
391
}
392
393
+ if (!copy && from_stdin) {
394
+ error("cannot use --stdin with %s subcommand.", argv[0]);
395
+ usage_with_options(git_notes_usage, options);
396
+ }
397
+
398
if (copy) {
399
const char *from_ref;
400
+ if (from_stdin) {
401
+ if (argc > 1) {
402
+ error("too many parameters");
403
+ usage_with_options(git_notes_usage, options);
404
+ } else {
405
+ return notes_copy_from_stdin(force);
406
+ }
407
+ }
408
if (argc < 3) {
409
error("too few parameters");
410
usage_with_options(git_notes_usage, options);
notes.c
+18
@@ -1185,3 +1185,21 @@ void format_display_notes(const unsigned char *object_sha1,
1185
format_note(display_notes_trees[i], object_sha1, sb,
1186
output_encoding, flags);
1187
}
1188
+
1189
+int copy_note(struct notes_tree *t,
1190
+ const unsigned char *from_obj, const unsigned char *to_obj,
1191
+ int force, combine_notes_fn combine_fn)
1192
+{
1193
+ const unsigned char *note = get_note(t, from_obj);
1194
+ const unsigned char *existing_note = get_note(t, to_obj);
1195
+
1196
+ if (!force && existing_note)
1197
+ return 1;
1198
+
1199
+ if (note)
1200
+ add_note(t, to_obj, note, combine_fn);
1201
+ else if (existing_note)
1202
+ add_note(t, to_obj, null_sha1, combine_fn);
1203
+
1204
+ return 0;
1205
+}
notes.h
+9
@@ -99,6 +99,15 @@ void remove_note(struct notes_tree *t, const unsigned char *object_sha1);
99
const unsigned char *get_note(struct notes_tree *t,
100
const unsigned char *object_sha1);
101
102
+/*
103
+ * Copy a note from one object to another in the given notes_tree.
104
+ *
105
+ * Fails if the to_obj already has a note unless 'force' is true.
106
+ */
107
+int copy_note(struct notes_tree *t,
108
+ const unsigned char *from_obj, const unsigned char *to_obj,
109
+ int force, combine_notes_fn combine_fn);
110
+
111
/*
112
* Flags controlling behaviour of for_each_note()
113
*
t/t3301-notes.sh
+34
@@ -776,4 +776,38 @@ test_expect_success 'cannot copy note from object without notes' '
776
test_must_fail git notes copy HEAD^ HEAD
777
'
778
779
+cat > expect << EOF
780
+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185
781
+Author: A U Thor <author@example.com>
782
+Date: Thu Apr 7 15:25:13 2005 -0700
783
+
784
+ 13th
785
+
786
+Notes (other):
787
+ yet another note
788
+$whitespace
789
+ yet another note
790
+
791
+commit 7038787dfe22a14c3867ce816dbba39845359719
792
+Author: A U Thor <author@example.com>
793
+Date: Thu Apr 7 15:24:13 2005 -0700
794
+
795
+ 12th
796
+
797
+Notes (other):
798
+ other note
799
+$whitespace
800
+ yet another note
801
+EOF
802
+
803
+test_expect_success 'git notes copy --stdin' '
804
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
805
+ echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
806
+ git notes copy --stdin &&
807
+ git log -2 > output &&
808
+ test_cmp expect output &&
809
+ test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
810
+ test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
811
+'
812
+
813
test_done