summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Thomas Rast <trast@student.ethz.ch>2010-03-12 18:04:31 +0100
committerLibravatar Junio C Hamano <gitster@pobox.com>2010-03-12 21:55:39 -0800
commit160baa0d9cbdfcdb6251aa5ede77c59c0d53edfd (patch)
tree278241bc0737a69c78be1a15655ff05c66261a46
parentrebase -i: invoke post-rewrite hook (diff)
downloadtgif-160baa0d9cbdfcdb6251aa5ede77c59c0d53edfd.tar.xz
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>
-rw-r--r--Documentation/git-notes.txt12
-rw-r--r--builtin-notes.c56
-rw-r--r--notes.c18
-rw-r--r--notes.h9
-rwxr-xr-xt/t3301-notes.sh34
5 files changed, 127 insertions, 2 deletions
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 7abd0fbd23..6ab3f982b9 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git notes' [list [<object>]]
'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
-'git notes' copy [-f] <from-object> <to-object>
+'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
'git notes' edit [<object>]
'git notes' show [<object>]
@@ -56,6 +56,16 @@ copy::
objects has none. (use -f to overwrite existing notes to the
second object). This subcommand is equivalent to:
`git notes add [-f] -C $(git notes list <from-object>) <to-object>`
++
+In `\--stdin` mode, take lines in the format
++
+----------
+<from-object> SP <to-object> [ SP <rest> ] LF
+----------
++
+on standard input, and copy the notes from each <from-object> to its
+corresponding <to-object>. (The optional `<rest>` is ignored so that
+the command can read the input given to the `post-rewrite` hook.)
append::
Append to the notes of an existing object (defaults to HEAD).
diff --git a/builtin-notes.c b/builtin-notes.c
index 123ecad830..daeb14e1d9 100644
--- a/builtin-notes.c
+++ b/builtin-notes.c
@@ -278,6 +278,46 @@ int commit_notes(struct notes_tree *t, const char *msg)
return 0;
}
+int notes_copy_from_stdin(int force)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct notes_tree *t;
+ int ret = 0;
+
+ init_notes(NULL, NULL, NULL, 0);
+ t = &default_notes_tree;
+
+ while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+ unsigned char from_obj[20], to_obj[20];
+ struct strbuf **split;
+ int err;
+
+ split = strbuf_split(&buf, ' ');
+ if (!split[0] || !split[1])
+ die("Malformed input line: '%s'.", buf.buf);
+ strbuf_rtrim(split[0]);
+ strbuf_rtrim(split[1]);
+ if (get_sha1(split[0]->buf, from_obj))
+ die("Failed to resolve '%s' as a valid ref.", split[0]->buf);
+ if (get_sha1(split[1]->buf, to_obj))
+ die("Failed to resolve '%s' as a valid ref.", split[1]->buf);
+
+ err = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);
+
+ if (err) {
+ error("Failed to copy notes from '%s' to '%s'",
+ split[0]->buf, split[1]->buf);
+ ret = 1;
+ }
+
+ strbuf_list_free(split);
+ }
+
+ commit_notes(t, "Notes added by 'git notes copy'");
+ free_notes(t);
+ return ret;
+}
+
int cmd_notes(int argc, const char **argv, const char *prefix)
{
struct notes_tree *t;
@@ -287,7 +327,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
char logmsg[100];
int list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,
- remove = 0, prune = 0, force = 0;
+ remove = 0, prune = 0, force = 0, from_stdin = 0;
int given_object = 0, i = 1, retval = 0;
struct msg_arg msg = { 0, 0, STRBUF_INIT };
struct option options[] = {
@@ -301,6 +341,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
OPT_CALLBACK('C', "reuse-message", &msg, "OBJECT",
"reuse specified note object", parse_reuse_arg),
OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
+ OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
OPT_END()
};
@@ -349,8 +390,21 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
usage_with_options(git_notes_usage, options);
}
+ if (!copy && from_stdin) {
+ error("cannot use --stdin with %s subcommand.", argv[0]);
+ usage_with_options(git_notes_usage, options);
+ }
+
if (copy) {
const char *from_ref;
+ if (from_stdin) {
+ if (argc > 1) {
+ error("too many parameters");
+ usage_with_options(git_notes_usage, options);
+ } else {
+ return notes_copy_from_stdin(force);
+ }
+ }
if (argc < 3) {
error("too few parameters");
usage_with_options(git_notes_usage, options);
diff --git a/notes.c b/notes.c
index 225a16608a..2feeb7bb06 100644
--- a/notes.c
+++ b/notes.c
@@ -1185,3 +1185,21 @@ void format_display_notes(const unsigned char *object_sha1,
format_note(display_notes_trees[i], object_sha1, sb,
output_encoding, flags);
}
+
+int copy_note(struct notes_tree *t,
+ const unsigned char *from_obj, const unsigned char *to_obj,
+ int force, combine_notes_fn combine_fn)
+{
+ const unsigned char *note = get_note(t, from_obj);
+ const unsigned char *existing_note = get_note(t, to_obj);
+
+ if (!force && existing_note)
+ return 1;
+
+ if (note)
+ add_note(t, to_obj, note, combine_fn);
+ else if (existing_note)
+ add_note(t, to_obj, null_sha1, combine_fn);
+
+ return 0;
+}
diff --git a/notes.h b/notes.h
index 2cc07409db..b7fafb448b 100644
--- a/notes.h
+++ b/notes.h
@@ -100,6 +100,15 @@ const unsigned char *get_note(struct notes_tree *t,
const unsigned char *object_sha1);
/*
+ * Copy a note from one object to another in the given notes_tree.
+ *
+ * Fails if the to_obj already has a note unless 'force' is true.
+ */
+int copy_note(struct notes_tree *t,
+ const unsigned char *from_obj, const unsigned char *to_obj,
+ int force, combine_notes_fn combine_fn);
+
+/*
* Flags controlling behaviour of for_each_note()
*
* Default behaviour of for_each_note() is to traverse every single note object
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index cb7166f6ec..60ad6a1675 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -776,4 +776,38 @@ test_expect_success 'cannot copy note from object without notes' '
test_must_fail git notes copy HEAD^ HEAD
'
+cat > expect << EOF
+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:25:13 2005 -0700
+
+ 13th
+
+Notes (other):
+ yet another note
+$whitespace
+ yet another note
+
+commit 7038787dfe22a14c3867ce816dbba39845359719
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:24:13 2005 -0700
+
+ 12th
+
+Notes (other):
+ other note
+$whitespace
+ yet another note
+EOF
+
+test_expect_success 'git notes copy --stdin' '
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+ echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+ git notes copy --stdin &&
+ git log -2 > output &&
+ test_cmp expect output &&
+ test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
+ test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
+'
+
test_done