Code

Merge branch 'nd/war-on-nul-in-commit'
authorJunio C Hamano <gitster@pobox.com>
Thu, 22 Dec 2011 19:27:26 +0000 (11:27 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 22 Dec 2011 19:27:26 +0000 (11:27 -0800)
* nd/war-on-nul-in-commit:
  commit_tree(): refuse commit messages that contain NULs
  Convert commit_tree() to take strbuf as message
  merge: abort if fails to commit

Conflicts:
builtin/commit.c
commit.c
commit.h

1  2 
builtin/commit-tree.c
builtin/commit.c
builtin/merge.c
builtin/notes.c
commit.c
commit.h
notes-merge.c

index b9c331225f0d5c960e9e03f2c92d498f8f01f74c,0895861457a2c90a0ddff9ab0864ef258e91cce5..05353c30f292684b44b42f7f744d8eb1b7062f69
@@@ -37,68 -37,26 +37,68 @@@ int cmd_commit_tree(int argc, const cha
  
        if (argc < 2 || !strcmp(argv[1], "-h"))
                usage(commit_tree_usage);
 -      if (get_sha1(argv[1], tree_sha1))
 -              die("Not a valid object name %s", argv[1]);
  
 -      for (i = 2; i < argc; i += 2) {
 -              unsigned char sha1[20];
 -              const char *a, *b;
 -              a = argv[i]; b = argv[i+1];
 -              if (!b || strcmp(a, "-p"))
 -                      usage(commit_tree_usage);
 +      for (i = 1; i < argc; i++) {
 +              const char *arg = argv[i];
 +              if (!strcmp(arg, "-p")) {
 +                      unsigned char sha1[20];
 +                      if (argc <= ++i)
 +                              usage(commit_tree_usage);
 +                      if (get_sha1(argv[i], sha1))
 +                              die("Not a valid object name %s", argv[i]);
 +                      assert_sha1_type(sha1, OBJ_COMMIT);
 +                      new_parent(lookup_commit(sha1), &parents);
 +                      continue;
 +              }
 +
 +              if (!strcmp(arg, "-m")) {
 +                      if (argc <= ++i)
 +                              usage(commit_tree_usage);
 +                      if (buffer.len)
 +                              strbuf_addch(&buffer, '\n');
 +                      strbuf_addstr(&buffer, argv[i]);
 +                      strbuf_complete_line(&buffer);
 +                      continue;
 +              }
 +
 +              if (!strcmp(arg, "-F")) {
 +                      int fd;
  
 -              if (get_sha1(b, sha1))
 -                      die("Not a valid object name %s", b);
 -              assert_sha1_type(sha1, OBJ_COMMIT);
 -              new_parent(lookup_commit(sha1), &parents);
 +                      if (argc <= ++i)
 +                              usage(commit_tree_usage);
 +                      if (buffer.len)
 +                              strbuf_addch(&buffer, '\n');
 +                      if (!strcmp(argv[i], "-"))
 +                              fd = 0;
 +                      else {
 +                              fd = open(argv[i], O_RDONLY);
 +                              if (fd < 0)
 +                                      die_errno("git commit-tree: failed to open '%s'",
 +                                                argv[i]);
 +                      }
 +                      if (strbuf_read(&buffer, fd, 0) < 0)
 +                              die_errno("git commit-tree: failed to read '%s'",
 +                                        argv[i]);
 +                      if (fd && close(fd))
 +                              die_errno("git commit-tree: failed to close '%s'",
 +                                        argv[i]);
 +                      strbuf_complete_line(&buffer);
 +                      continue;
 +              }
 +
 +              if (get_sha1(arg, tree_sha1))
 +                      die("Not a valid object name %s", arg);
 +              if (got_tree)
 +                      die("Cannot give more than one trees");
 +              got_tree = 1;
        }
  
 -      if (strbuf_read(&buffer, 0, 0) < 0)
 -              die_errno("git commit-tree: failed to read");
 +      if (!buffer.len) {
 +              if (strbuf_read(&buffer, 0, 0) < 0)
 +                      die_errno("git commit-tree: failed to read");
 +      }
  
-       if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
+       if (commit_tree(&buffer, tree_sha1, parents, commit_sha1, NULL)) {
                strbuf_release(&buffer);
                return 1;
        }
index be1ab2e2570fe0b600f8b1aa55c32573578af766,849151e9517818203b9d7ba4368c87d26dd61ebf..3069041b8023224be0080f081d852dc001a81437
@@@ -1485,15 -1483,8 +1485,15 @@@ int cmd_commit(int argc, const char **a
                exit(1);
        }
  
 -      if (commit_tree(&sb, active_cache_tree->sha1, parents, sha1,
 -                      author_ident.buf)) {
 +      if (amend) {
 +              extra = read_commit_extra_headers(current_head);
 +      } else {
 +              struct commit_extra_header **tail = &extra;
 +              append_merge_tag_headers(parents, &tail);
 +      }
 +
-       if (commit_tree_extended(sb.buf, active_cache_tree->sha1, parents, sha1,
++      if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
 +                               author_ident.buf, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
diff --cc builtin/merge.c
Simple merge
diff --cc builtin/notes.c
Simple merge
diff --cc commit.c
index b78127403be65f5c26e35e53a59b91860396194a,80e61b4cf551a05b06f5944018265868767515bf..44bc96d44d391209d3a60e9e8c1b98ed209e6b4a
+++ b/commit.c
@@@ -840,160 -840,14 +840,160 @@@ struct commit_list *reduce_heads(struc
        return result;
  }
  
- int commit_tree(const char *msg, unsigned char *tree,
 +static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
 +{
 +      struct merge_remote_desc *desc;
 +      struct commit_extra_header *mergetag;
 +      char *buf;
 +      unsigned long size, len;
 +      enum object_type type;
 +
 +      desc = merge_remote_util(parent);
 +      if (!desc || !desc->obj)
 +              return;
 +      buf = read_sha1_file(desc->obj->sha1, &type, &size);
 +      if (!buf || type != OBJ_TAG)
 +              goto free_return;
 +      len = parse_signature(buf, size);
 +      if (size == len)
 +              goto free_return;
 +      /*
 +       * We could verify this signature and either omit the tag when
 +       * it does not validate, but the integrator may not have the
 +       * public key of the signer of the tag he is merging, while a
 +       * later auditor may have it while auditing, so let's not run
 +       * verify-signed-buffer here for now...
 +       *
 +       * if (verify_signed_buffer(buf, len, buf + len, size - len, ...))
 +       *      warn("warning: signed tag unverified.");
 +       */
 +      mergetag = xcalloc(1, sizeof(*mergetag));
 +      mergetag->key = xstrdup("mergetag");
 +      mergetag->value = buf;
 +      mergetag->len = size;
 +
 +      **tail = mergetag;
 +      *tail = &mergetag->next;
 +      return;
 +
 +free_return:
 +      free(buf);
 +}
 +
 +void append_merge_tag_headers(struct commit_list *parents,
 +                            struct commit_extra_header ***tail)
 +{
 +      while (parents) {
 +              struct commit *parent = parents->item;
 +              handle_signed_tag(parent, tail);
 +              parents = parents->next;
 +      }
 +}
 +
 +static void add_extra_header(struct strbuf *buffer,
 +                           struct commit_extra_header *extra)
 +{
 +      strbuf_addstr(buffer, extra->key);
 +      if (extra->len)
 +              strbuf_add_lines(buffer, " ", extra->value, extra->len);
 +      else
 +              strbuf_addch(buffer, '\n');
 +}
 +
 +struct commit_extra_header *read_commit_extra_headers(struct commit *commit)
 +{
 +      struct commit_extra_header *extra = NULL;
 +      unsigned long size;
 +      enum object_type type;
 +      char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
 +      if (buffer && type == OBJ_COMMIT)
 +              extra = read_commit_extra_header_lines(buffer, size);
 +      free(buffer);
 +      return extra;
 +}
 +
 +static inline int standard_header_field(const char *field, size_t len)
 +{
 +      return ((len == 4 && !memcmp(field, "tree ", 5)) ||
 +              (len == 6 && !memcmp(field, "parent ", 7)) ||
 +              (len == 6 && !memcmp(field, "author ", 7)) ||
 +              (len == 9 && !memcmp(field, "committer ", 10)) ||
 +              (len == 8 && !memcmp(field, "encoding ", 9)));
 +}
 +
 +struct commit_extra_header *read_commit_extra_header_lines(const char *buffer, size_t size)
 +{
 +      struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
 +      const char *line, *next, *eof, *eob;
 +      struct strbuf buf = STRBUF_INIT;
 +
 +      for (line = buffer, eob = line + size;
 +           line < eob && *line != '\n';
 +           line = next) {
 +              next = memchr(line, '\n', eob - line);
 +              next = next ? next + 1 : eob;
 +              if (*line == ' ') {
 +                      /* continuation */
 +                      if (it)
 +                              strbuf_add(&buf, line + 1, next - (line + 1));
 +                      continue;
 +              }
 +              if (it)
 +                      it->value = strbuf_detach(&buf, &it->len);
 +              strbuf_reset(&buf);
 +              it = NULL;
 +
 +              eof = strchr(line, ' ');
 +              if (next <= eof)
 +                      eof = next;
 +
 +              if (standard_header_field(line, eof - line))
 +                      continue;
 +
 +              it = xcalloc(1, sizeof(*it));
 +              it->key = xmemdupz(line, eof-line);
 +              *tail = it;
 +              tail = &it->next;
 +              if (eof + 1 < next)
 +                      strbuf_add(&buf, eof + 1, next - (eof + 1));
 +      }
 +      if (it)
 +              it->value = strbuf_detach(&buf, &it->len);
 +      return extra;
 +}
 +
 +void free_commit_extra_headers(struct commit_extra_header *extra)
 +{
 +      while (extra) {
 +              struct commit_extra_header *next = extra->next;
 +              free(extra->key);
 +              free(extra->value);
 +              free(extra);
 +              extra = next;
 +      }
 +}
 +
++int commit_tree(const struct strbuf *msg, unsigned char *tree,
 +              struct commit_list *parents, unsigned char *ret,
 +              const char *author)
 +{
 +      struct commit_extra_header *extra = NULL, **tail = &extra;
 +      int result;
 +
 +      append_merge_tag_headers(parents, &tail);
 +      result = commit_tree_extended(msg, tree, parents, ret, author, extra);
 +      free_commit_extra_headers(extra);
 +      return result;
 +}
 +
  static const char commit_utf8_warn[] =
  "Warning: commit message does not conform to UTF-8.\n"
  "You may want to amend it after fixing the message, or set the config\n"
  "variable i18n.commitencoding to the encoding your project uses.\n";
  
- int commit_tree_extended(const char *msg, unsigned char *tree,
 -int commit_tree(const struct strbuf *msg, unsigned char *tree,
 -              struct commit_list *parents, unsigned char *ret,
 -              const char *author)
++int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
 +                       struct commit_list *parents, unsigned char *ret,
 +                       const char *author, struct commit_extra_header *extra)
  {
        int result;
        int encoding_is_utf8;
diff --cc commit.h
index 3745f120994cb6876e1ccc1bfc5e07c6cc3fc5c9,5cf46b2e0a5485757a23c86ed38b4d2de6c1342a..4df397865abe7161a9f16e3177e3b486bf8434ca
+++ b/commit.h
@@@ -181,41 -181,8 +181,41 @@@ static inline int single_parent(struct 
  
  struct commit_list *reduce_heads(struct commit_list *heads);
  
- extern int commit_tree(const char *msg, unsigned char *tree,
 +struct commit_extra_header {
 +      struct commit_extra_header *next;
 +      char *key;
 +      char *value;
 +      size_t len;
 +};
 +
 +extern void append_merge_tag_headers(struct commit_list *parents,
 +                                   struct commit_extra_header ***tail);
 +
 -              struct commit_list *parents, unsigned char *ret,
 -              const char *author);
+ extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
- extern int commit_tree_extended(const char *msg, unsigned char *tree,
 +                     struct commit_list *parents, unsigned char *ret,
 +                     const char *author);
 +
++extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
 +                              struct commit_list *parents, unsigned char *ret,
 +                              const char *author,
 +                              struct commit_extra_header *);
 +
 +extern struct commit_extra_header *read_commit_extra_headers(struct commit *);
 +extern struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len);
 +
 +extern void free_commit_extra_headers(struct commit_extra_header *extra);
 +
 +struct merge_remote_desc {
 +      struct object *obj; /* the named object, could be a tag */
 +      const char *name;
 +};
 +#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
 +
 +/*
 + * Given "name" from the command line to merge, find the commit object
 + * and return it, while storing merge_remote_desc in its ->util field,
 + * to allow callers to tell if we are told to merge a tag.
 + */
 +struct commit *get_merge_parent(const char *name);
  
  #endif /* COMMIT_H */
diff --cc notes-merge.c
Simple merge