builtin / commit-tree.con commit Merge branch 'js/rebase-reschedule-applies-only-to-interactive' into maint (8eb5097)
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7#include "config.h"
   8#include "object-store.h"
   9#include "repository.h"
  10#include "commit.h"
  11#include "tree.h"
  12#include "builtin.h"
  13#include "utf8.h"
  14#include "gpg-interface.h"
  15#include "parse-options.h"
  16
  17static const char * const commit_tree_usage[] = {
  18        N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] "
  19                "[(-F <file>)...] <tree>"),
  20        NULL
  21};
  22
  23static const char *sign_commit;
  24
  25static void new_parent(struct commit *parent, struct commit_list **parents_p)
  26{
  27        struct object_id *oid = &parent->object.oid;
  28        struct commit_list *parents;
  29        for (parents = *parents_p; parents; parents = parents->next) {
  30                if (parents->item == parent) {
  31                        error(_("duplicate parent %s ignored"), oid_to_hex(oid));
  32                        return;
  33                }
  34                parents_p = &parents->next;
  35        }
  36        commit_list_insert(parent, parents_p);
  37}
  38
  39static int commit_tree_config(const char *var, const char *value, void *cb)
  40{
  41        int status = git_gpg_config(var, value, NULL);
  42        if (status)
  43                return status;
  44        return git_default_config(var, value, cb);
  45}
  46
  47static int parse_parent_arg_callback(const struct option *opt,
  48                const char *arg, int unset)
  49{
  50        struct object_id oid;
  51        struct commit_list **parents = opt->value;
  52
  53        BUG_ON_OPT_NEG_NOARG(unset, arg);
  54
  55        if (get_oid_commit(arg, &oid))
  56                die(_("not a valid object name %s"), arg);
  57
  58        assert_oid_type(&oid, OBJ_COMMIT);
  59        new_parent(lookup_commit(the_repository, &oid), parents);
  60        return 0;
  61}
  62
  63static int parse_message_arg_callback(const struct option *opt,
  64                const char *arg, int unset)
  65{
  66        struct strbuf *buf = opt->value;
  67
  68        BUG_ON_OPT_NEG_NOARG(unset, arg);
  69
  70        if (buf->len)
  71                strbuf_addch(buf, '\n');
  72        strbuf_addstr(buf, arg);
  73        strbuf_complete_line(buf);
  74
  75        return 0;
  76}
  77
  78static int parse_file_arg_callback(const struct option *opt,
  79                const char *arg, int unset)
  80{
  81        int fd;
  82        struct strbuf *buf = opt->value;
  83
  84        BUG_ON_OPT_NEG_NOARG(unset, arg);
  85
  86        if (buf->len)
  87                strbuf_addch(buf, '\n');
  88        if (!strcmp(arg, "-"))
  89                fd = 0;
  90        else {
  91                fd = open(arg, O_RDONLY);
  92                if (fd < 0)
  93                        die_errno(_("git commit-tree: failed to open '%s'"), arg);
  94        }
  95        if (strbuf_read(buf, fd, 0) < 0)
  96                die_errno(_("git commit-tree: failed to read '%s'"), arg);
  97        if (fd && close(fd))
  98                die_errno(_("git commit-tree: failed to close '%s'"), arg);
  99
 100        return 0;
 101}
 102
 103int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 104{
 105        static struct strbuf buffer = STRBUF_INIT;
 106        struct commit_list *parents = NULL;
 107        struct object_id tree_oid;
 108        struct object_id commit_oid;
 109
 110        struct option options[] = {
 111                { OPTION_CALLBACK, 'p', NULL, &parents, N_("parent"),
 112                        N_("id of a parent commit object"), PARSE_OPT_NONEG,
 113                        parse_parent_arg_callback },
 114                { OPTION_CALLBACK, 'm', NULL, &buffer, N_("message"),
 115                        N_("commit message"), PARSE_OPT_NONEG,
 116                        parse_message_arg_callback },
 117                { OPTION_CALLBACK, 'F', NULL, &buffer, N_("file"),
 118                        N_("read commit log message from file"), PARSE_OPT_NONEG,
 119                        parse_file_arg_callback },
 120                { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 121                        N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 122                OPT_END()
 123        };
 124
 125        git_config(commit_tree_config, NULL);
 126
 127        if (argc < 2 || !strcmp(argv[1], "-h"))
 128                usage_with_options(commit_tree_usage, options);
 129
 130        argc = parse_options(argc, argv, prefix, options, commit_tree_usage, 0);
 131
 132        if (argc != 1)
 133                die(_("must give exactly one tree"));
 134
 135        if (get_oid_tree(argv[0], &tree_oid))
 136                die(_("not a valid object name %s"), argv[0]);
 137
 138        if (!buffer.len) {
 139                if (strbuf_read(&buffer, 0, 0) < 0)
 140                        die_errno(_("git commit-tree: failed to read"));
 141        }
 142
 143        if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid,
 144                        NULL, sign_commit)) {
 145                strbuf_release(&buffer);
 146                return 1;
 147        }
 148
 149        printf("%s\n", oid_to_hex(&commit_oid));
 150        strbuf_release(&buffer);
 151        return 0;
 152}