+static void cmd_new_tag()
+{
+ char *str_uq;
+ const char *endp;
+ char *sp;
+ const char *from;
+ char *tagger;
+ struct branch *s;
+ void *msg;
+ size_t msglen;
+ char *body;
+ struct tag *t;
+ unsigned char sha1[20];
+
+ /* Obtain the new tag name from the rest of our command */
+ sp = strchr(command_buf.buf, ' ') + 1;
+ str_uq = unquote_c_style(sp, &endp);
+ if (str_uq) {
+ if (*endp)
+ die("Garbage after tag name in: %s", command_buf.buf);
+ sp = str_uq;
+ }
+ t = pool_alloc(sizeof(struct tag));
+ t->next_tag = NULL;
+ t->name = pool_strdup(sp);
+ if (last_tag)
+ last_tag->next_tag = t;
+ else
+ first_tag = t;
+ last_tag = t;
+ if (str_uq)
+ free(str_uq);
+ read_next_command();
+
+ /* from ... */
+ if (strncmp("from ", command_buf.buf, 5))
+ die("Expected from command, got %s", command_buf.buf);
+
+ from = strchr(command_buf.buf, ' ') + 1;
+ str_uq = unquote_c_style(from, &endp);
+ if (str_uq) {
+ if (*endp)
+ die("Garbage after string in: %s", command_buf.buf);
+ from = str_uq;
+ }
+
+ s = lookup_branch(from);
+ if (s) {
+ memcpy(sha1, s->sha1, 20);
+ } else if (*from == ':') {
+ unsigned long idnum = strtoul(from + 1, NULL, 10);
+ struct object_entry *oe = find_mark(idnum);
+ if (oe->type != OBJ_COMMIT)
+ die("Mark :%lu not a commit", idnum);
+ memcpy(sha1, oe->sha1, 20);
+ } else if (!get_sha1(from, sha1)) {
+ unsigned long size;
+ char *buf;
+
+ buf = read_object_with_reference(sha1,
+ type_names[OBJ_COMMIT], &size, sha1);
+ if (!buf || size < 46)
+ die("Not a valid commit: %s", from);
+ free(buf);
+ } else
+ die("Invalid ref name or SHA1 expression: %s", from);
+
+ if (str_uq)
+ free(str_uq);
+ read_next_command();
+
+ /* tagger ... */
+ if (strncmp("tagger ", command_buf.buf, 7))
+ die("Expected tagger command, got %s", command_buf.buf);
+ tagger = strdup(command_buf.buf);
+
+ /* tag payload/message */
+ read_next_command();
+ msg = cmd_data(&msglen);
+
+ /* build the tag object */
+ body = xmalloc(67 + strlen(t->name) + strlen(tagger) + msglen);
+ sp = body;
+ sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1));
+ sp += sprintf(sp, "type %s\n", type_names[OBJ_COMMIT]);
+ sp += sprintf(sp, "tag %s\n", t->name);
+ sp += sprintf(sp, "%s\n\n", tagger);
+ memcpy(sp, msg, msglen);
+ sp += msglen;
+ free(tagger);
+ free(msg);
+
+ store_object(OBJ_TAG, body, sp - body, NULL, t->sha1, 0);
+ free(body);
+}
+